Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn on strict typing in mypy #2211

Merged
merged 5 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ max_line_length = 88 # Same as Black
[*.md]
trim_trailing_whitespace = false

[*.{yaml,yml,toml}]
[*.{yaml,yml,json,toml}]
indent_size = 2
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@ jobs:
strategy:
fail-fast: false
matrix:
# mypy 1.5 dropped support for python 3.7
# mypy 1.5 dropped support for Python 3.7
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: pip install types-regex types-setuptools mypy>=1.5
- run: pip install types-regex types-setuptools mypy==1.9
- run: mypy . --python-version=${{ matrix.python-version }}

pyright:
Expand Down
2 changes: 1 addition & 1 deletion com/win32com/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import win32api

# flag if we are in a "frozen" build.
_frozen = getattr(sys, "frozen", 1 == 0)
_frozen = getattr(sys, "frozen", False)
# pythoncom dumbly defaults this to zero - we believe sys.frozen over it.
if _frozen and not getattr(pythoncom, "frozen", 0):
pythoncom.frozen = sys.frozen
Expand Down
61 changes: 43 additions & 18 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,33 +1,53 @@
[mypy]
show_column_numbers = true
warn_unused_ignores = true
; Target the oldest supported version in editors
python_version = 3.7
; Target the oldest supported version in editors and default CLI
; mypy 1.5 dropped support for Python 3.7
python_version = 3.8

strict = false
strict = true
implicit_reexport = true
; Necessary to avoid "same module name" issues
explicit_package_bases = true
; Must specify top-level packages and scripts folders for mypy to work with explicit_package_bases
mypy_path =
$MYPY_CONFIG_FILE_DIR/com,
$MYPY_CONFIG_FILE_DIR/win32/Lib,
$MYPY_CONFIG_FILE_DIR/Pythonwin,
$MYPY_CONFIG_FILE_DIR/AutoDuck,
$MYPY_CONFIG_FILE_DIR/win32/scripts/VersionStamp,

; Implicit return types !
; TODO: turn back check_untyped_defs to true. For now this allows us to
; at least put mypy in place by massively reducing checked code
; TODO: Gradually type classes and functions until we can turn back check_untyped_defs to true.
; For now this allows us to at least put mypy in place by massively reducing checked code
check_untyped_defs = false
; Implicit return types !
disallow_untyped_calls = false
disallow_untyped_defs = false
disallow_incomplete_defs = false

; attr-defined: Module has no attribute (modules are dynamic)
; method-assign: Cannot assign to a method (lots of monkey patching)
; name-defined: Name "..." is not defined (dynamic modules will be hard to type without stubs, ie: pythoncom.*, leave undefined/unbound to Flake8/Ruff/pyright)
disable_error_code = attr-defined, method-assign, name-defined
; TODO: adodbapi should be updated and fixed separatly
; Pythonwin/Scintilla is vendored
; 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
; Ignoring non-public apis for now
; Duplicate module named "rasutil" and "setup", short-term fix is to ignore
exclude = .*((build|adodbapi|Pythonwin/Scintilla|Pythonwin/pywin/idle|[Tt]est|[Dd]emos?)/.*|rasutil.py|setup.py)
disable_error_code =
; Module has no attribute; (Dynamic modules will be hard to type without first-party stubs, ie: pythoncom.*)
attr-defined,
; Class cannot subclass "..." (has type "Any"); (IDEM)
; TODO: Use typeshed's types-pywin32 stubs after a few more fixes there
misc,
Comment on lines +30 to +32
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

; Name "..." is not defined; (IDEM, leave undefined/unbound to Flake8/Ruff/pyright)
name-defined,
; Cannot assign to a method (we do lots of monkey patching)
method-assign,
exclude = (?x)(
^build/
; Vendored
| ^Pythonwin/Scintilla/
; Forked IDLE extensions predating Python 2.3. They now live in idlelib in https://github.com/python/cpython/tree/main/Lib/idlelib
| ^Pythonwin/pywin/idle/
; TODO: adodbapi should be updated and fixed separatly
| ^adodbapi/
; TODO: Ignoring non-public APIs until all public API is typed
| ([Tt]est|[Dd]emos?)/
)

; C-modules that will need type-stubs
[mypy-adsi.*,dde,exchange,exchdapi,perfmon,servicemanager,win32api,win32console,win32clipboard,win32event,win32evtlog,win32file,win32gui,win32help,win32pdh,win32process,win32ras,win32security,win32service,win32trace,win32ui,win32uiole,win32wnet,_win32sysloader,_winxptheme]
[mypy-adsi.*,dde,exchange,exchdapi,mapi,perfmon,servicemanager,win32api,win32console,win32clipboard,win32comext.adsi.adsi,win32event,win32evtlog,win32file,win32gui,win32help,win32pdh,win32process,win32ras,win32security,win32service,win32trace,win32ui,win32uiole,win32wnet,_win32sysloader,_winxptheme]
ignore_missing_imports = True

; verstamp is installed from win32verstamp.py called in setup.py
Expand All @@ -36,3 +56,8 @@ ignore_missing_imports = True
; pywin32_system32 is an empty module created in setup.py to store dlls
[mypy-verstamp,win32com.*,Test,pywin32_system32]
ignore_missing_imports = True

; Distutils being removed from stdlib currently causes some issues on Python 3.12
; https://github.com/mhammond/pywin32/issues/2119
[mypy-distutils.*]
ignore_missing_imports = True
8 changes: 4 additions & 4 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"typeCheckingMode": "basic",
// Target the oldest supported version in editors
// Target the oldest supported version in editors and default CLI
"pythonVersion": "3.7",
// Keep it simple for now by allowing both mypy and pyright to use `type: ignore`
"enableTypeIgnoreComments": true,
// Exclude from scanning when running pyright
"exclude": [
"build/",
// TODO: adodbapi should be updated and fixed separatly
"adodbapi/",
// Vendored
"Pythonwin/Scintilla/",
// Vendored IDLE extensions predating Python 2.3. They now live in idlelib in https://github.com/python/cpython/tree/main/Lib/idlelib
// Forked IDLE extensions predating Python 2.3. They now live in idlelib in https://github.com/python/cpython/tree/main/Lib/idlelib
"Pythonwin/pywin/idle/",
// Ignoring non-public apis for now
"**/Test/",
"**/test/",
"**/Demos/",
"**/demo/",
// TODO: adodbapi should be updated and fixed separately
"adodbapi/",
],
// Packages that will be accessible globally.
// Setting this makes pyright use the repo's code for those modules instead of typeshed or pywin32 in site-packages
Expand Down
4 changes: 1 addition & 3 deletions pywin32_postinstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,7 @@ def LoadSystemModule(lib_dir, modname):
loader = importlib.machinery.ExtensionFileLoader(modname, filename)
spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module( # pyright: ignore[reportOptionalMemberAccess] # We provide the loader, we know it won't be None
mod
)
loader.exec_module(mod)


def SetPyKeyVal(key_name, value_name, value):
Expand Down
30 changes: 14 additions & 16 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

build_id = "306" # may optionally include a ".{patchno}" suffix.

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

from distutils import ccompiler
from distutils._msvccompiler import MSVCCompiler
from distutils.command.install_data import install_data

if sys.version_info >= (3, 8):
Expand Down Expand Up @@ -936,22 +940,17 @@ def my_new_compiler(**kw):


# No way to cleanly wedge our compiler sub-class in.
from distutils import ccompiler
from distutils._msvccompiler import MSVCCompiler

orig_new_compiler = ccompiler.new_compiler
ccompiler.new_compiler = my_new_compiler

base_compiler = MSVCCompiler
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't look like base_compiler was used for anything other than being an alias.

ccompiler.new_compiler = my_new_compiler # type: ignore[assignment] # Assuming the caller will always use only kwargs


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

def link(
self,
Expand Down Expand Up @@ -1112,7 +1111,7 @@ def finalize_options(self):
pch_header="PyWinTypes.h",
)

win32_extensions = [pywintypes]
win32_extensions: list[WinExt] = [pywintypes]

win32_extensions.append(
WinExt_win32(
Expand Down Expand Up @@ -1254,11 +1253,10 @@ def finalize_options(self):
windows_h_ver = info[2]
if len(info) > 3:
sources = info[3].split()
extra_compile_args = []
ext = WinExt_win32(
name,
libraries=lib_names,
extra_compile_args=extra_compile_args,
extra_compile_args=[],
windows_h_version=windows_h_ver,
sources=sources,
)
Expand Down Expand Up @@ -1425,8 +1423,8 @@ def finalize_options(self):
base_address=dll_base_address,
)
dll_base_address += 0x80000 # pythoncom is large!
com_extensions = [pythoncom]
com_extensions += [
com_extensions = [
pythoncom,
WinExt_win32com(
"adsi",
libraries="ACTIVEDS ADSIID user32 advapi32",
Expand Down Expand Up @@ -2089,7 +2087,7 @@ def finalize_options(self):
swig_include_files = "mapilib adsilib".split()


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

Expand All @@ -2104,7 +2102,7 @@ def convert_data_files(files: Iterable[str]):
for file in files:
file = os.path.normpath(file)
if file.find("*") >= 0:
files_use = (
files_use = tuple(
str(path)
for path in Path(file).parent.rglob(os.path.basename(file))
# We never want CVS
Expand Down
10 changes: 6 additions & 4 deletions win32/Lib/win32timezone.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: UTF-8 -*-
from __future__ import annotations

"""
win32timezone:
Expand Down Expand Up @@ -231,7 +232,6 @@
datetime.datetime(2011, 11, 6, 1, 0, tzinfo=TimeZoneInfo('Pacific Standard Time'))

"""
from __future__ import annotations

import datetime
import logging
Expand All @@ -240,6 +240,7 @@
import struct
import winreg
from itertools import count
from typing import Dict

import win32api

Expand Down Expand Up @@ -792,8 +793,8 @@ def get_sorted_time_zones(key=None):
return zones


class _RegKeyDict(dict):
def __init__(self, key):
class _RegKeyDict(Dict[str, int]):
def __init__(self, key: winreg.HKEYType):
dict.__init__(self)
self.key = key
self.__load_values()
Expand Down Expand Up @@ -907,7 +908,8 @@ def resolveMUITimeZone(spec):


# from jaraco.util.dictlib 5.3.1
class RangeMap(dict):
# TODO: Update to implementation in jaraco.collections
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class RangeMap(dict): # type: ignore[type-arg] # Source code is untyped :/ TODO: Add generics!
"""
A dictionary-like object that uses the keys as bounds for a range.
Inclusion of the value for that range is determined by the
Expand Down
Loading