diff --git a/mypy.ini b/mypy.ini index 69dca8f198..e6e782b0a7 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,6 +7,9 @@ python_version = 3.9 strict = true implicit_reexport = true +allow_redefinition = true +allow_redefinition_new = true +local_partial_types = 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 @@ -26,31 +29,21 @@ disallow_untyped_defs = false disallow_incomplete_defs = false disable_error_code = - ; Module has no attribute; (Dynamic modules will be hard to type without first-party stubs, ie: pythoncom.*) + ; Module has no attribute; (Dynamic modules will be hard to type without type 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, ; These are the other error codes that would currently fail with check_untyped_defs = true + ; and have no PR already open to fix them ; TODO: Gradually fix them until we can turn on check_untyped_defs ; arg-type, ; assignment, - ; call-arg, - ; comparison-overlap, ; has-type, ; index, - ; list-item, ; operator, ; override, - ; str-format, - ; type-var, ; union-attr, - ; valid-type, ; var-annotated, - ; ; And these only happen when checking against types-pywin32 - ; func-returns-value, - ; call-overload, - ; no-redef, exclude = (?x)( ^build/ ; Vendored diff --git a/pyproject.toml b/pyproject.toml index 68afe01095..a6d056f7f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,10 @@ checkers = [ # Keep these in sync with .pre-commit-config.yaml "ruff ==0.14.14", ] type-checkers = [ - "types-setuptools", + "types-setuptools >=80.9.0.20251221", "PyOpenGL", - "mypy ==1.16.*; python_version >='3.9'", - "pyright ==1.1.401", + "mypy ==1.19.*", + "pyright ==1.1.407", ] dev = [ "pre-commit", diff --git a/pyrightconfig.json b/pyrightconfig.json index 6bf7f9184f..254fc650c2 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,5 +1,5 @@ { - "typeCheckingMode": "basic", + "typeCheckingMode": "standard", // Needs to be set for non-Windows CI runners "pythonPlatform": "Windows", // Target the oldest supported version in editors and default CLI @@ -29,17 +29,18 @@ ], "reportConstantRedefinition": "error", // TODO: For now this allows us to at least put pyright in place by massively reducing checked code - // it also reduces issues with the shipped types-pywin32 from typeshed - "reportGeneralTypeIssues": "none", "reportArgumentType": "none", "reportAttributeAccessIssue": "none", + "reportIncompatibleMethodOverride": "none", // FIXE: These all need to be fixed first and turned back to error "reportCallIssue": "warning", + "reportGeneralTypeIssues": "warning", "reportOperatorIssue": "warning", "reportOptionalCall": "warning", "reportOptionalIterable": "warning", "reportOptionalMemberAccess": "warning", "reportOptionalSubscript": "warning", + "reportPossiblyUnboundVariable": "warning", // Too many dynamically generated modules. This will require type stubs to properly fix. "reportMissingImports": "warning", // IDEM, but happens when pywin32 is not in site-packages but module is found from typeshed. diff --git a/pythonwin/pywin/framework/dbgcommands.py b/pythonwin/pywin/framework/dbgcommands.py index fda0251955..58aa2c833e 100644 --- a/pythonwin/pywin/framework/dbgcommands.py +++ b/pythonwin/pywin/framework/dbgcommands.py @@ -64,7 +64,7 @@ def _DoOrStart(self, doMethod, startFlag): method() else: scriptutils.RunScript( - defName=None, defArgs=None, bShowDialog=0, debuggingType=startFlag + defName=None, defArgs=None, bShowDialog=False, debuggingType=startFlag ) def OnStep(self, msg, code): diff --git a/pythonwin/pywin/framework/intpyapp.py b/pythonwin/pywin/framework/intpyapp.py index c69fa184fd..cf19ace9c5 100644 --- a/pythonwin/pywin/framework/intpyapp.py +++ b/pythonwin/pywin/framework/intpyapp.py @@ -327,29 +327,18 @@ def ProcessArgs(self, args: Sequence[str], dde=None): dde.Exec(f"win32ui.GetApp().OpenDocumentFile({fname!r})") else: win32ui.GetApp().OpenDocumentFile(par) - elif argType == "/rundlg": + elif argType in {"/rundlg", "/run"}: + defArgs = " ".join(args[i + 1 :]) + showDialog = argType == "/rundlg" if dde: dde.Exec( - "from pywin.framework import scriptutils;scriptutils.RunScript({!r}, {!r}, 1)".format( - par, " ".join(args[i + 1 :]) - ) + "from pywin.framework import scriptutils;" + + f"scriptutils.RunScript({par!r}, {defArgs!r}, {showDialog!r})" ) else: from . import scriptutils - scriptutils.RunScript(par, " ".join(args[i + 1 :])) - return - elif argType == "/run": - if dde: - dde.Exec( - "from pywin.framework import scriptutils;scriptutils.RunScript({!r}, {!r}, 0)".format( - par, " ".join(args[i + 1 :]) - ) - ) - else: - from . import scriptutils - - scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0) + scriptutils.RunScript(par, defArgs, showDialog) return elif argType == "/app": raise RuntimeError( diff --git a/pythonwin/pywin/framework/scriptutils.py b/pythonwin/pywin/framework/scriptutils.py index abf5a40c13..272f6431f0 100644 --- a/pythonwin/pywin/framework/scriptutils.py +++ b/pythonwin/pywin/framework/scriptutils.py @@ -2,6 +2,8 @@ Various utilities for running/importing a script """ +from __future__ import annotations + import bdb import linecache import os @@ -211,9 +213,14 @@ def GetActiveFileName(bAutoSave=1): lastDebuggingType = RS_DEBUGGER_NONE -def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None): +def RunScript( + defName: str | None = None, + defArgs: str | None = None, + bShowDialog: bool = True, + debuggingType: int | None = None, +): global lastScript, lastArgs, lastDebuggingType - _debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me! + _debugger_stop_frame_ = True # Magic variable so the debugger will hide me! # Get the debugger - may be None! debugger = GetDebugger() @@ -309,7 +316,7 @@ def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None): f = open(script, "rb") except OSError as exc: win32ui.MessageBox( - "The file could not be opened - %s (%d)" % (exc.strerror, exc.errno) + f"The file could not be opened - {exc.strerror} ({exc.errno})" ) return @@ -408,7 +415,7 @@ def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None): win32ui.DoWaitCursor(0) -def ImportFile(): +def ImportFile() -> None: """This code looks for the current window, and determines if it can be imported. If not, it will prompt for a file name, and allow it to be imported.""" try: @@ -427,20 +434,19 @@ def ImportFile(): ) dlg.SetOFNTitle("Import Script") if dlg.DoModal() != win32con.IDOK: - return 0 + return pathName = dlg.GetPathName() # If already imported, don't look for package path, modName = os.path.split(pathName) modName, modExt = os.path.splitext(modName) - newPath = None # note that some packages (*cough* email *cough*) use "lazy importers" # meaning sys.modules can change as a side-effect of looking at # module.__file__ - so we must take a copy (ie, list(items())) - for key, mod in sys.modules.items(): - if getattr(mod, "__file__", None): - fname = mod.__file__ + for key, mod in list(sys.modules.items()): + fname = getattr(mod, "__file__", None) + if fname: base, ext = os.path.splitext(fname) if ext.lower() in (".pyo", ".pyc"): ext = ".py" @@ -454,15 +460,15 @@ def ImportFile(): sys.path.append(newPath) if modName in sys.modules: - bNeedReload = 1 + bNeedReload = True what = "reload" else: what = "import" - bNeedReload = 0 + bNeedReload = False win32ui.SetStatusText(what.capitalize() + "ing module...", 1) win32ui.DoWaitCursor(1) - # win32ui.GetMainFrame().BeginWaitCursor() + # win32ui.GetMainFrame().BeginWaitCursor() try: # always do an import, as it is cheap if it's already loaded. This ensures diff --git a/pythonwin/pywin/framework/toolmenu.py b/pythonwin/pywin/framework/toolmenu.py index 2666468993..f91a89a5ff 100644 --- a/pythonwin/pywin/framework/toolmenu.py +++ b/pythonwin/pywin/framework/toolmenu.py @@ -1,12 +1,10 @@ -# toolmenu.py - import sys import win32api import win32con import win32ui -tools = {} +tools: dict[int, tuple[str, str, str]] = {} idPos = 100 # The default items should no tools menu exist in the INI file. @@ -73,7 +71,6 @@ def WriteToolMenuItems(items): def SetToolsMenu(menu, menuPos=None): - global tools global idPos # todo - check the menu does not already exist. @@ -111,7 +108,6 @@ def HandleToolCommand(cmd, code): import re import traceback - global tools (menuString, pyCmd, desc) = tools[cmd] win32ui.SetStatusText("Executing tool %s" % desc, 1) pyCmd = re.sub(r"\\n", "\n", pyCmd) diff --git a/pythonwin/pywin/scintilla/keycodes.py b/pythonwin/pywin/scintilla/keycodes.py index 44c8ee6ec6..2508872fad 100644 --- a/pythonwin/pywin/scintilla/keycodes.py +++ b/pythonwin/pywin/scintilla/keycodes.py @@ -1,12 +1,8 @@ import win32api import win32con -import win32ui MAPVK_VK_TO_CHAR = 2 -key_name_to_vk = {} -key_code_to_name = {} - _better_names = { "escape": "esc", "return": "enter", @@ -14,21 +10,19 @@ "next": "pgdn", } - -def _fillvkmap(): - # Pull the VK_names from win32con - names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")] - for name in names: - code = getattr(win32con, name) - n = name[3:].lower() +# Pull the VK_names from win32con +key_name_to_vk: dict[str, int] = {} +key_code_to_name: dict[int, str] = {} +for __name in win32con.__dict__: + if not __name.startswith("VK_"): + continue + code = getattr(win32con, __name) + n = __name[3:].lower() + key_name_to_vk[n] = code + if n in _better_names: + n = _better_names[n] key_name_to_vk[n] = code - if n in _better_names: - n = _better_names[n] - key_name_to_vk[n] = code - key_code_to_name[code] = n - - -_fillvkmap() + key_code_to_name[code] = n def get_vk(chardesc): diff --git a/pythonwin/pywin/scintilla/view.py b/pythonwin/pywin/scintilla/view.py index bc816808fa..6367686646 100644 --- a/pythonwin/pywin/scintilla/view.py +++ b/pythonwin/pywin/scintilla/view.py @@ -1,4 +1,4 @@ -# A general purpose MFC CCtrlView view that uses Scintilla. +"""A general purpose MFC CCtrlView view that uses Scintilla.""" import os import re @@ -26,44 +26,42 @@ patImport = re.compile(r"import (?P.*)") -_event_commands = [ +event_commands = [ # File menu - "win32ui.ID_FILE_LOCATE", - "win32ui.ID_FILE_CHECK", - "afxres.ID_FILE_CLOSE", - "afxres.ID_FILE_NEW", - "afxres.ID_FILE_OPEN", - "afxres.ID_FILE_SAVE", - "afxres.ID_FILE_SAVE_AS", - "win32ui.ID_FILE_SAVE_ALL", + ("FileLocate", win32ui.ID_FILE_LOCATE), + ("FileCheck", win32ui.ID_FILE_CHECK), + ("FileClose", afxres.ID_FILE_CLOSE), + ("FileNew", afxres.ID_FILE_NEW), + ("FileOpen", afxres.ID_FILE_OPEN), + ("FileSave", afxres.ID_FILE_SAVE), + ("FileSaveAs", afxres.ID_FILE_SAVE_AS), + ("FileSaveAll", win32ui.ID_FILE_SAVE_ALL), # Edit menu - "afxres.ID_EDIT_UNDO", - "afxres.ID_EDIT_REDO", - "afxres.ID_EDIT_CUT", - "afxres.ID_EDIT_COPY", - "afxres.ID_EDIT_PASTE", - "afxres.ID_EDIT_SELECT_ALL", - "afxres.ID_EDIT_FIND", - "afxres.ID_EDIT_REPEAT", - "afxres.ID_EDIT_REPLACE", + ("EditUndo", afxres.ID_EDIT_UNDO), + ("EditRedo", afxres.ID_EDIT_REDO), + ("EditCut", afxres.ID_EDIT_CUT), + ("EditCopy", afxres.ID_EDIT_COPY), + ("EditPaste", afxres.ID_EDIT_PASTE), + ("EditSelectAll", afxres.ID_EDIT_SELECT_ALL), + ("EditFind", afxres.ID_EDIT_FIND), + ("EditRepeat", afxres.ID_EDIT_REPEAT), + ("EditReplace", afxres.ID_EDIT_REPLACE), # View menu - "win32ui.ID_VIEW_WHITESPACE", - "win32ui.ID_VIEW_FIXED_FONT", - "win32ui.ID_VIEW_BROWSE", - "win32ui.ID_VIEW_INTERACTIVE", + ("ViewWhitespace", win32ui.ID_VIEW_WHITESPACE), + ("ViewFixedFont", win32ui.ID_VIEW_FIXED_FONT), + ("ViewBrowse", win32ui.ID_VIEW_BROWSE), + ("ViewInteractive", win32ui.ID_VIEW_INTERACTIVE), # Window menu - "afxres.ID_WINDOW_ARRANGE", - "afxres.ID_WINDOW_CASCADE", - "afxres.ID_WINDOW_NEW", - "afxres.ID_WINDOW_SPLIT", - "afxres.ID_WINDOW_TILE_HORZ", - "afxres.ID_WINDOW_TILE_VERT", + ("WindowArrange", afxres.ID_WINDOW_ARRANGE), + ("WindowCascade", afxres.ID_WINDOW_CASCADE), + ("WindowNew", afxres.ID_WINDOW_NEW), + ("WindowSplit", afxres.ID_WINDOW_SPLIT), + ("WindowTileHorz", afxres.ID_WINDOW_TILE_HORZ), + ("WindowTileVert", afxres.ID_WINDOW_TILE_VERT), # Others - "afxres.ID_APP_EXIT", - "afxres.ID_APP_ABOUT", -] - -_extra_event_commands = [ + ("AppExit", afxres.ID_APP_EXIT), + ("AppAbout", afxres.ID_APP_ABOUT), + # Extra event commands ("EditDelete", afxres.ID_EDIT_CLEAR), ("LocateModule", win32ui.ID_FILE_LOCATE), ("GotoLine", win32ui.ID_EDIT_GOTO_LINE), @@ -76,24 +74,6 @@ ("DbgClose", win32ui.IDC_DBG_CLOSE), ] -event_commands = [] - - -def _CreateEvents(): - for name in _event_commands: - val = eval(name) - name_parts = name.split("_")[1:] - name_parts = [p.capitalize() for p in name_parts] - event = "".join(name_parts) - event_commands.append((event, val)) - for name, id in _extra_event_commands: - event_commands.append((name, id)) - - -_CreateEvents() -del _event_commands -del _extra_event_commands - command_reflectors = [ (win32ui.ID_EDIT_UNDO, win32con.WM_UNDO), (win32ui.ID_EDIT_REDO, scintillacon.SCI_REDO), diff --git a/pythonwin/win32uimodule.cpp b/pythonwin/win32uimodule.cpp index f19facaf92..a1536670c9 100644 --- a/pythonwin/win32uimodule.cpp +++ b/pythonwin/win32uimodule.cpp @@ -1570,7 +1570,7 @@ static PyObject *ui_set_registry_key(PyObject *self, PyObject *args) RETURN_NONE; } -// @pymethod |win32ui|GetAppRegistryKey|Returns the registry key for the application. +// @pymethod PyHKEY|win32ui|GetAppRegistryKey|Returns the registry key for the application. static PyObject *ui_get_app_registry_key(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":GetRegistryKey")) diff --git a/win32/Lib/sspi.py b/win32/Lib/sspi.py index 7ac621fd87..9870a2464c 100644 --- a/win32/Lib/sspi.py +++ b/win32/Lib/sspi.py @@ -14,6 +14,8 @@ # Based on Roger Upole's sspi demos. # $Id$ +from __future__ import annotations + import sspicon import win32security diff --git a/win32/Lib/win32serviceutil.py b/win32/Lib/win32serviceutil.py index 3276bc61b0..b56002c4e9 100644 --- a/win32/Lib/win32serviceutil.py +++ b/win32/Lib/win32serviceutil.py @@ -5,6 +5,7 @@ # (which is win32service.error, pywintypes.error, etc) # when things go wrong - eg, not enough permissions to hit the # registry etc. +from __future__ import annotations import importlib.machinery import os @@ -571,6 +572,9 @@ def RestartService(serviceName, args=None, waitSeconds=30, machine=None): print("Gave up waiting for the old service to stop!") +g_debugService: ServiceFramework | None = None + + def _DebugCtrlHandler(evt): if evt in (win32con.CTRL_C_EVENT, win32con.CTRL_BREAK_EVENT): assert g_debugService diff --git a/win32/scripts/pywin32_testall.py b/win32/scripts/pywin32_testall.py index 8cbcfe675c..a5a95e59c3 100644 --- a/win32/scripts/pywin32_testall.py +++ b/win32/scripts/pywin32_testall.py @@ -1,22 +1,25 @@ """A test runner for pywin32""" +from __future__ import annotations + import os import site import subprocess import sys +from collections.abc import Iterable # locate the dirs based on where this script is - it may be either in the # source tree, or in an installed Python 'Scripts' tree. project_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) site_packages = [site.getusersitepackages()] + site.getsitepackages() -failures = [] +failures: list[str | os.PathLike[str]] = [] # Run a test using subprocess and wait for the result. # If we get an returncode != 0, we know that there was an error, but we don't # abort immediately - we run as many tests as we can. -def run_test(script, cmdline_extras): +def run_test(script: str | os.PathLike[str], cmdline_extras): dirname, scriptname = os.path.split(script) # some tests prefer to be run from their directory. cmd = [sys.executable, "-u", scriptname] + cmdline_extras @@ -29,7 +32,7 @@ def run_test(script, cmdline_extras): failures.append(script) -def find_and_run(possible_locations, extras): +def find_and_run(possible_locations: Iterable[str | os.PathLike[str]], extras): for maybe in possible_locations: if os.path.isfile(maybe): run_test(maybe, extras) diff --git a/win32/src/win32pdhmodule.cpp b/win32/src/win32pdhmodule.cpp index a7feff75c4..25684512ff 100644 --- a/win32/src/win32pdhmodule.cpp +++ b/win32/src/win32pdhmodule.cpp @@ -341,7 +341,7 @@ static PyObject *PyCloseQuery(PyObject *self, PyObject *args) return Py_None; } -// @pymethod |win32pdh|MakeCounterPath|Makes a fully resolved counter path +// @pymethod str|win32pdh|MakeCounterPath|Makes a fully resolved counter path static PyObject *PyMakeCounterPath(PyObject *self, PyObject *args) { PyObject *ret = NULL;