Skip to content

Commit 153a5fc

Browse files
committed
Turn on strict typing in mypy
1 parent 7cac4eb commit 153a5fc

File tree

8 files changed

+63
-44
lines changed

8 files changed

+63
-44
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ max_line_length = 88 # Same as Black
1515
[*.md]
1616
trim_trailing_whitespace = false
1717

18-
[*.{yaml,yml}]
18+
[*.{yaml,yml,json}]
1919
indent_size = 2

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,14 @@ jobs:
116116
strategy:
117117
fail-fast: false
118118
matrix:
119-
# mypy 1.5 dropped support for python 3.7
119+
# mypy 1.5 dropped support for Python 3.7
120120
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
121121
steps:
122122
- uses: actions/checkout@v3
123123
- uses: actions/setup-python@v4
124124
with:
125125
python-version: ${{ matrix.python-version }}
126-
- run: pip install types-regex types-setuptools mypy>=1.5
126+
- run: pip install types-regex types-setuptools mypy==1.9
127127
- run: mypy . --python-version=${{ matrix.python-version }}
128128

129129
pyright:

com/win32com/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import win32api
1010

1111
# flag if we are in a "frozen" build.
12-
_frozen = getattr(sys, "frozen", 1 == 0)
12+
_frozen = getattr(sys, "frozen", False)
1313
# pythoncom dumbly defaults this to zero - we believe sys.frozen over it.
1414
if _frozen and not getattr(pythoncom, "frozen", 0):
1515
pythoncom.frozen = sys.frozen

mypy.ini

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,46 @@
11
[mypy]
22
show_column_numbers = true
33
warn_unused_ignores = true
4-
; Target the oldest supported version in editors
5-
python_version = 3.7
4+
; Target the oldest supported version in editors and default CLI
5+
; mypy 1.5 dropped support for Python 3.7
6+
python_version = 3.8
67

7-
strict = false
8+
strict = true
89
implicit_reexport = true
910

10-
; Implicit return types !
11-
; TODO: turn back check_untyped_defs to true. For now this allows us to
12-
; at least put mypy in place by massively reducing checked code
11+
; TODO: Gradually type classes and functions until we can turn back check_untyped_defs to true.
12+
; For now this allows us to at least put mypy in place by massively reducing checked code
1313
check_untyped_defs = false
14+
; Implicit return types !
1415
disallow_untyped_calls = false
1516
disallow_untyped_defs = false
1617
disallow_incomplete_defs = false
1718

18-
; attr-defined: Module has no attribute (modules are dynamic)
19-
; method-assign: Cannot assign to a method (lots of monkey patching)
20-
; name-defined: Name "..." is not defined (dynamic modules will be hard to type without stubs, ie: pythoncom.*, leave undefined/unbound to Flake8/Ruff/pyright)
21-
disable_error_code = attr-defined, method-assign, name-defined
22-
; TODO: adodbapi should be updated and fixed separatly
23-
; Pythonwin/Scintilla is vendored
24-
; Pythonwin/pywin/idle is vendored IDLE extensions predating Python 2.3. They now live in idlelib in https://github.com/python/cpython/tree/main/Lib/idlelib
25-
; Ignoring non-public apis for now
26-
; Duplicate module named "rasutil" and "setup", short-term fix is to ignore
27-
exclude = .*((build|adodbapi|Pythonwin/Scintilla|Pythonwin/pywin/idle|[Tt]est|[Dd]emos?)/.*|rasutil.py|setup.py)
19+
disable_error_code =
20+
; Module has no attribute; (Dynamic modules will be hard to type without first-party stubs, ie: pythoncom.*)
21+
attr-defined,
22+
; Class cannot subclass "..." (has type "Any"); (IDEM)
23+
; TODO: Use typeshed's types-pywin32 stubs after a few more fixes there
24+
misc,
25+
; Name "..." is not defined; (IDEM, leave undefined/unbound to Flake8/Ruff/pyright)
26+
name-defined,
27+
; Cannot assign to a method (we do lots of monkey patching)
28+
method-assign,
29+
30+
exclude = (?x)(
31+
^build/
32+
; Vendored
33+
|^Pythonwin/Scintilla/
34+
; Pythonwin/pywin/idle is forked IDLE extensions predating Python 2.3. They now live in idlelib in https://github.com/python/cpython/tree/main/Lib/idlelib
35+
|^Pythonwin/pywin/idle/
36+
; Duplicate module named "rasutil" and "setup", short-term fix is to ignore
37+
|^win32/scripts/rasutil.py$
38+
|^win32/Demos/c_extension/setup.py
39+
; TODO: adodbapi should be updated and fixed separatly
40+
|^adodbapi/
41+
; TODO: Ignoring non-public APIs until all public API is typed
42+
|([Tt]est|[Dd]emos?)/
43+
)
2844

2945
; C-modules that will need type-stubs
3046
[mypy-adsi.*,dde,exchange,exchdapi,perfmon,servicemanager,win32api,win32clipboard,win32event,win32evtlog,win32file,win32gui,win32help,win32pdh,win32process,win32ras,win32security,win32service,win32trace,win32ui,win32uiole,win32wnet,wincerapi,winxpgui,_win32sysloader,_winxptheme]
@@ -36,3 +52,8 @@ ignore_missing_imports = True
3652
; pywin32_system32 is an empty module created in setup.py to store dlls
3753
[mypy-verstamp,win32com.*,Test,pywin32_system32]
3854
ignore_missing_imports = True
55+
56+
; Distutils being removed from stdlib currently causes some issues on Python 3.12
57+
; https://github.com/mhammond/pywin32/issues/2119
58+
[mypy-distutils.*]
59+
ignore_missing_imports = True

pyrightconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"typeCheckingMode": "basic",
3-
// Target the oldest supported version in editors
3+
// Target the oldest supported version in editors and default CLI
44
"pythonVersion": "3.7",
55
// Keep it simple for now by allowing both mypy and pyright to use `type: ignore`
66
"enableTypeIgnoreComments": true,

pywin32_postinstall.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,7 @@ def LoadSystemModule(lib_dir, modname):
188188
loader = importlib.machinery.ExtensionFileLoader(modname, filename)
189189
spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename)
190190
mod = importlib.util.module_from_spec(spec)
191-
spec.loader.exec_module( # pyright: ignore[reportOptionalMemberAccess] # We provide the loader, we know it won't be None
192-
mod
193-
)
191+
loader.exec_module(mod)
194192

195193

196194
def SetPyKeyVal(key_name, value_name, value):

setup.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
build_id = "306" # may optionally include a ".{patchno}" suffix.
24

35
__doc__ = """This is a distutils setup-script for the pywin32 extensions.
@@ -44,6 +46,8 @@
4446
from setuptools.command.install import install
4547
from setuptools.command.install_lib import install_lib
4648

49+
from distutils import ccompiler
50+
from distutils._msvccompiler import MSVCCompiler
4751
from distutils.command.install_data import install_data
4852

4953
if sys.version_info >= (3, 8):
@@ -967,22 +971,17 @@ def my_new_compiler(**kw):
967971

968972

969973
# No way to cleanly wedge our compiler sub-class in.
970-
from distutils import ccompiler
971-
from distutils._msvccompiler import MSVCCompiler
972-
973974
orig_new_compiler = ccompiler.new_compiler
974-
ccompiler.new_compiler = my_new_compiler
975-
976-
base_compiler = MSVCCompiler
975+
ccompiler.new_compiler = my_new_compiler # type: ignore[assignment] # Assuming the caller will always use only kwargs
977976

978977

979-
class my_compiler(base_compiler):
978+
class my_compiler(MSVCCompiler):
980979
# Just one GUIDS.CPP and it gives trouble on mainwin too. Maybe I
981980
# should just rename the file, but a case-only rename is likely to be
982981
# worse! This can probably go away once we kill the VS project files
983982
# though, as we can just specify the lowercase name in the module def.
984-
_cpp_extensions = base_compiler._cpp_extensions + [".CPP"]
985-
src_extensions = base_compiler.src_extensions + [".CPP"]
983+
_cpp_extensions = MSVCCompiler._cpp_extensions + [".CPP"]
984+
src_extensions = MSVCCompiler.src_extensions + [".CPP"]
986985

987986
def link(
988987
self,
@@ -1146,7 +1145,7 @@ def finalize_options(self):
11461145
pch_header="PyWinTypes.h",
11471146
)
11481147

1149-
win32_extensions = [pywintypes]
1148+
win32_extensions: list[WinExt] = [pywintypes]
11501149

11511150
win32_extensions.append(
11521151
WinExt_win32(
@@ -1288,11 +1287,10 @@ def finalize_options(self):
12881287
windows_h_ver = info[2]
12891288
if len(info) > 3:
12901289
sources = info[3].split()
1291-
extra_compile_args = []
12921290
ext = WinExt_win32(
12931291
name,
12941292
libraries=lib_names,
1295-
extra_compile_args=extra_compile_args,
1293+
extra_compile_args=[],
12961294
windows_h_version=windows_h_ver,
12971295
sources=sources,
12981296
)
@@ -1472,8 +1470,8 @@ def finalize_options(self):
14721470
base_address=dll_base_address,
14731471
)
14741472
dll_base_address += 0x80000 # pythoncom is large!
1475-
com_extensions = [pythoncom]
1476-
com_extensions += [
1473+
com_extensions = [
1474+
pythoncom,
14771475
WinExt_win32com(
14781476
"adsi",
14791477
libraries="ACTIVEDS ADSIID user32 advapi32",
@@ -2136,7 +2134,7 @@ def finalize_options(self):
21362134
swig_include_files = "mapilib adsilib".split()
21372135

21382136

2139-
def expand_modules(module_dir: Union[str, os.PathLike]):
2137+
def expand_modules(module_dir: Union[str, os.PathLike[str]]):
21402138
"""Helper to allow our script specifications to include wildcards."""
21412139
return [str(path.with_suffix("")) for path in Path(module_dir).rglob("*.py")]
21422140

@@ -2151,7 +2149,7 @@ def convert_data_files(files: Iterable[str]):
21512149
for file in files:
21522150
file = os.path.normpath(file)
21532151
if file.find("*") >= 0:
2154-
files_use = (
2152+
files_use = tuple(
21552153
str(path)
21562154
for path in Path(file).parent.rglob(os.path.basename(file))
21572155
# We never want CVS

win32/Lib/win32timezone.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: UTF-8 -*-
2+
from __future__ import annotations
23

34
"""
45
win32timezone:
@@ -231,7 +232,6 @@
231232
datetime.datetime(2011, 11, 6, 1, 0, tzinfo=TimeZoneInfo('Pacific Standard Time'))
232233
233234
"""
234-
from __future__ import annotations
235235

236236
import datetime
237237
import logging
@@ -240,6 +240,7 @@
240240
import struct
241241
import winreg
242242
from itertools import count
243+
from typing import Dict
243244

244245
import win32api
245246

@@ -792,8 +793,8 @@ def get_sorted_time_zones(key=None):
792793
return zones
793794

794795

795-
class _RegKeyDict(dict):
796-
def __init__(self, key):
796+
class _RegKeyDict(Dict[str, int]):
797+
def __init__(self, key: winreg.HKEYType):
797798
dict.__init__(self)
798799
self.key = key
799800
self.__load_values()
@@ -907,7 +908,8 @@ def resolveMUITimeZone(spec):
907908

908909

909910
# from jaraco.util.dictlib 5.3.1
910-
class RangeMap(dict):
911+
# TODO: Update to implementation in jaraco.collections
912+
class RangeMap(dict): # type: ignore[type-arg] # Source code is untyped :/ TODO: Add generics!
911913
"""
912914
A dictionary-like object that uses the keys as bounds for a range.
913915
Inclusion of the value for that range is determined by the

0 commit comments

Comments
 (0)