From 9a420560cf6bcde4f97ad3ae0c79ae260a594d38 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 27 Jul 2024 04:02:08 -0400 Subject: [PATCH 1/2] mypy: resolve many str-format, has-type, valid-type, var-annotated, valid-type, type-var --- AutoDuck/makedfromi.py | 2 +- AutoDuck/py2d.py | 4 ++-- Pythonwin/pywin/debugger/dbgcon.py | 2 +- Pythonwin/pywin/framework/help.py | 2 +- Pythonwin/pywin/framework/interact.py | 2 +- Pythonwin/pywin/framework/mdi_pychecker.py | 14 ++++++------- Pythonwin/pywin/framework/scriptutils.py | 7 +++---- Pythonwin/pywin/framework/winout.py | 4 ++-- Pythonwin/pywin/mfc/dialog.py | 8 +++---- Pythonwin/pywin/scintilla/config.py | 8 ++++--- Pythonwin/pywin/scintilla/view.py | 18 +++++++--------- com/win32com/client/dynamic.py | 2 +- com/win32com/client/gencache.py | 6 ++---- com/win32comext/axdebug/codecontainer.py | 6 ++++-- com/win32comext/axscript/client/pyscript.py | 5 ++++- mypy.ini | 20 ++++++++++++++++-- win32/Lib/win32pdhquery.py | 23 +++++++++++---------- win32/Lib/win32pdhutil.py | 4 ++-- win32/Lib/win32serviceutil.py | 19 +++++++++-------- 19 files changed, 85 insertions(+), 71 deletions(-) diff --git a/AutoDuck/makedfromi.py b/AutoDuck/makedfromi.py index 4fd2f44ac..ef6ea9345 100644 --- a/AutoDuck/makedfromi.py +++ b/AutoDuck/makedfromi.py @@ -32,7 +32,7 @@ def GetComments(line, lineNo, lines): def make_doc_summary(inFile, outFile): - methods = [] + methods: list[tuple[str, list[str]]] = [] modDoc = "" modName = "" lines = inFile.readlines() diff --git a/AutoDuck/py2d.py b/AutoDuck/py2d.py index 6a0452bf7..63f2fcb3c 100644 --- a/AutoDuck/py2d.py +++ b/AutoDuck/py2d.py @@ -52,8 +52,8 @@ def should_build_function(build_info): # docstring aware paragraph generator. Isn't there something in docutils # we can use? -def gen_paras(val): - chunks = [] +def gen_paras(val: str): + chunks: list[str] = [] in_docstring = False for line in val.splitlines(): line = ad_escape(line.strip()) diff --git a/Pythonwin/pywin/debugger/dbgcon.py b/Pythonwin/pywin/debugger/dbgcon.py index 402ec64b3..44046eca2 100644 --- a/Pythonwin/pywin/debugger/dbgcon.py +++ b/Pythonwin/pywin/debugger/dbgcon.py @@ -21,7 +21,7 @@ def DoGetOption(optsDict, optName, default): def LoadDebuggerOptions(): - opts = {} + opts: dict[str, str] = {} DoGetOption(opts, OPT_HIDE, 0) DoGetOption(opts, OPT_STOP_EXCEPTIONS, 1) return opts diff --git a/Pythonwin/pywin/framework/help.py b/Pythonwin/pywin/framework/help.py index a042b1865..f38bf7895 100644 --- a/Pythonwin/pywin/framework/help.py +++ b/Pythonwin/pywin/framework/help.py @@ -79,7 +79,7 @@ def ListAllHelpFiles(): def _ListAllHelpFilesInRoot(root): """Returns a list of (helpDesc, helpFname) for all registered help files""" - retList = [] + retList: list[tuple[str, str]] = [] try: key = win32api.RegOpenKey( root, regutil.BuildDefaultPythonKey() + "\\Help", 0, win32con.KEY_READ diff --git a/Pythonwin/pywin/framework/interact.py b/Pythonwin/pywin/framework/interact.py index d93934e0e..102e5cbf5 100644 --- a/Pythonwin/pywin/framework/interact.py +++ b/Pythonwin/pywin/framework/interact.py @@ -482,7 +482,7 @@ def GetBlockBoundary(self, lineNo): def ExtractCommand(self, lines): start, end = lines - retList = [] + retList: list[str] = [] while end >= start: thisLine = self.DoGetLine(end) promptLen = len(GetPromptPrefix(thisLine)) diff --git a/Pythonwin/pywin/framework/mdi_pychecker.py b/Pythonwin/pywin/framework/mdi_pychecker.py index 03e5e220f..220949a8d 100644 --- a/Pythonwin/pywin/framework/mdi_pychecker.py +++ b/Pythonwin/pywin/framework/mdi_pychecker.py @@ -61,18 +61,18 @@ def getsubdirs(d): class dirpath: def __init__(self, str, recurse=0): dp = str.split(";") - dirs = {} + dirs = set() for d in dp: if os.path.isdir(d): d = d.lower() if d not in dirs: - dirs[d] = None + dirs.add(d) if recurse: subdirs = getsubdirs(d) for sd in subdirs: sd = sd.lower() if sd not in dirs: - dirs[sd] = None + dirs.add(sd) elif os.path.isfile(d): pass else: @@ -105,16 +105,14 @@ def __init__(self, str, recurse=0): if x: for xd in x: if xd not in dirs: - dirs[xd] = None + dirs.add(xd) if recurse: subdirs = getsubdirs(xd) for sd in subdirs: sd = sd.lower() if sd not in dirs: - dirs[sd] = None - self.dirs = [] - for d in dirs.keys(): - self.dirs.append(d) + dirs.add(sd) + self.dirs = list(dirs) def __getitem__(self, key): return self.dirs[key] diff --git a/Pythonwin/pywin/framework/scriptutils.py b/Pythonwin/pywin/framework/scriptutils.py index 98c34b477..e3cc310d4 100644 --- a/Pythonwin/pywin/framework/scriptutils.py +++ b/Pythonwin/pywin/framework/scriptutils.py @@ -441,7 +441,7 @@ def ImportFile(): # 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 list(sys.modules.items()): - if getattr(mod, "__file__", None): + if hasattr(mod, "__file__") and mod.__file__: fname = mod.__file__ base, ext = os.path.splitext(fname) if ext.lower() in (".pyo", ".pyc"): @@ -549,14 +549,13 @@ def RunTabNanny(filename): data = newout.getvalue() if data: try: - lineno = data.split()[1] - lineno = int(lineno) + lineno = int(data.split()[1]) _JumpToPosition(filename, lineno) try: # Try and display whitespace GetActiveEditControl().SCISetViewWS(1) except: pass - win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno) + win32ui.SetStatusText(f"The TabNanny found trouble at line {lineno}") except (IndexError, TypeError, ValueError): print("The tab nanny complained, but I can't see where!") print(data) diff --git a/Pythonwin/pywin/framework/winout.py b/Pythonwin/pywin/framework/winout.py index c98c98ff8..6be5a6001 100644 --- a/Pythonwin/pywin/framework/winout.py +++ b/Pythonwin/pywin/framework/winout.py @@ -380,7 +380,7 @@ def __init__( self.iniSizeSection = None self.defSize = defSize self.currentView = None - self.outputQueue = queue.Queue(-1) + self.outputQueue: queue.Queue[str] = queue.Queue(-1) self.mainThreadId = win32api.GetCurrentThreadId() self.idleHandlerSet = 0 self.SetIdleHandler() @@ -520,7 +520,7 @@ def QueueFlush(self, max=None): self.currentView.dowrite("".join(items)) return rc - def HandleOutput(self, message): + def HandleOutput(self, message: str): # debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message )) self.outputQueue.put(message) if win32api.GetCurrentThreadId() != self.mainThreadId: diff --git a/Pythonwin/pywin/mfc/dialog.py b/Pythonwin/pywin/mfc/dialog.py index 93a5ba650..7caa44eb1 100644 --- a/Pythonwin/pywin/mfc/dialog.py +++ b/Pythonwin/pywin/mfc/dialog.py @@ -258,13 +258,11 @@ def GetSimpleInput(prompt, defValue="", title=None): # uses a simple dialog to return a string object. if title is None: title = win32ui.GetMainFrame().GetWindowText() - # 2to3 insists on converting 'Dialog.__init__' to 'tkinter.dialog...' - DlgBaseClass = Dialog - class DlgSimpleInput(DlgBaseClass): + class DlgSimpleInput(Dialog): def __init__(self, prompt, defValue, title): self.title = title - DlgBaseClass.__init__(self, win32ui.IDD_SIMPLE_INPUT) + Dialog.__init__(self, win32ui.IDD_SIMPLE_INPUT) self.AddDDX(win32ui.IDC_EDIT1, "result") self.AddDDX(win32ui.IDC_PROMPT1, "prompt") self._obj_.data["result"] = defValue @@ -272,7 +270,7 @@ def __init__(self, prompt, defValue, title): def OnInitDialog(self): self.SetWindowText(self.title) - return DlgBaseClass.OnInitDialog(self) + return Dialog.OnInitDialog(self) dlg = DlgSimpleInput(prompt, defValue, title) if dlg.DoModal() != win32con.IDOK: diff --git a/Pythonwin/pywin/scintilla/config.py b/Pythonwin/pywin/scintilla/config.py index 6f5a6396f..f93d96402 100644 --- a/Pythonwin/pywin/scintilla/config.py +++ b/Pythonwin/pywin/scintilla/config.py @@ -8,6 +8,8 @@ # .py file, and put the config info in a docstring. Then # pass a CStringIO file (rather than a filename) to the # config manager. +from __future__ import annotations + import glob import importlib.util import marshal @@ -35,14 +37,14 @@ def trace(*args): compiled_config_version = 3 -def split_line(line, lineno): +def split_line(line: str, lineno: int): comment_pos = line.find("#") if comment_pos >= 0: line = line[:comment_pos] sep_pos = line.rfind("=") if sep_pos == -1: if line.strip(): - print("Warning: Line %d: %s is an invalid entry" % (lineno, repr(line))) + print(f"Warning: Line {lineno}: {line!r} is an invalid entry") return None, None return "", "" return line[:sep_pos].strip(), line[sep_pos + 1 :].strip() @@ -271,7 +273,7 @@ def _save_data(self, name, data): return data def _load_general(self, sub_section, fp, lineno): - map = {} + map: dict[str, list[str | None]] = {} while 1: line, lineno, bBreak = self._readline(fp, lineno) if bBreak: diff --git a/Pythonwin/pywin/scintilla/view.py b/Pythonwin/pywin/scintilla/view.py index e94148750..68a28910e 100644 --- a/Pythonwin/pywin/scintilla/view.py +++ b/Pythonwin/pywin/scintilla/view.py @@ -462,12 +462,6 @@ def SaveTextFile(self, filename, encoding=None): return 1 def _AutoComplete(self): - def list2dict(l): - ret = {} - for i in l: - ret[i] = None - return ret - self.SCIAutoCCancel() # Cancel old auto-complete lists. # First try and get an object without evaluating calls ob = self._GetObjectAtPos(bAllowCalls=0) @@ -480,17 +474,19 @@ def list2dict(l): # extra attributes of win32ui objects if hasattr(ob, "_obj_"): try: - items_dict.update(list2dict(dir(ob._obj_))) + items_dict.update(dict.fromkeys(dir(ob._obj_))) except AttributeError: pass # object has no __dict__ # normal attributes try: - items_dict.update(list2dict(dir(ob))) + items_dict.update(dict.fromkeys(dir(ob))) except AttributeError: pass # object has no __dict__ if hasattr(ob, "__class__"): - items_dict.update(list2dict(_get_class_attributes(ob.__class__))) + items_dict.update( + dict.fromkeys(_get_class_attributes(ob.__class__)) + ) # The object may be a COM object with typelib support - let's see if we can get its props. # (contributed by Stefan Migowsky) try: @@ -672,8 +668,8 @@ def _GetWordSplit(self, pos=-1, bAllowCalls=0): if pos == -1: pos = self.GetSel()[0] - 1 # Character before current one limit = self.GetTextLength() - before = [] - after = [] + before: list[str] = [] + after: list[str] = [] index = pos - 1 wordbreaks_use = wordbreaks if bAllowCalls: diff --git a/com/win32com/client/dynamic.py b/com/win32com/client/dynamic.py index ffe7e9e26..fc0208a1d 100644 --- a/com/win32com/client/dynamic.py +++ b/com/win32com/client/dynamic.py @@ -409,7 +409,7 @@ def _make_method_(self, name): # self._print_details_() codeObject = compile(methodCode, "" % self._username_, "exec") # Exec the code object - tempNameSpace = {} + tempNameSpace: dict[str, object] = {} # "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours. globNameSpace = globals().copy() globNameSpace["Dispatch"] = win32com.client.Dispatch diff --git a/com/win32com/client/gencache.py b/com/win32com/client/gencache.py index 385478561..06720ab16 100644 --- a/com/win32com/client/gencache.py +++ b/com/win32com/client/gencache.py @@ -760,10 +760,8 @@ def Rebuild(verbose=1): def _Dump(): print("Cache is in directory", win32com.__gen_path__) # Build a unique dir - d = {} - for clsid, (typelibCLSID, lcid, major, minor) in clsidToTypelib.items(): - d[typelibCLSID, lcid, major, minor] = None - for typelibCLSID, lcid, major, minor in d.keys(): + d = set(clsidToTypelib.values()) + for typelibCLSID, lcid, major, minor in d: mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor) print(f"{mod.__doc__} - {typelibCLSID}") diff --git a/com/win32comext/axdebug/codecontainer.py b/com/win32comext/axdebug/codecontainer.py index d82ee0008..5d543b1b7 100644 --- a/com/win32comext/axdebug/codecontainer.py +++ b/com/win32comext/axdebug/codecontainer.py @@ -4,6 +4,8 @@ to color the text, and also how to translate lines into offsets, and back. """ +from __future__ import annotations + import os import sys import tokenize @@ -124,7 +126,7 @@ def _ProcessToken(self, type, token, spos, epos, line): erow, ecol = epos self.GetText() # Prime us. linenum = srow - 1 # Lines zero based for us too. - realCharPos = self.lineOffsets[linenum] + scol + realCharPos: int = self.lineOffsets[linenum] + scol numskipped = realCharPos - self.lastPos if numskipped == 0: pass @@ -157,7 +159,7 @@ def _ProcessToken(self, type, token, spos, epos, line): def GetSyntaxColorAttributes(self): self.lastPos = 0 - self.attrs = [] + self.attrs: list[tuple[int] | tuple[int, int]] = [] try: for tokens in tokenize.tokenize(self.GetNextLine): self._ProcessToken(*tokens) diff --git a/com/win32comext/axscript/client/pyscript.py b/com/win32comext/axscript/client/pyscript.py index 5bf46eb80..53744b8d2 100644 --- a/com/win32comext/axscript/client/pyscript.py +++ b/com/win32comext/axscript/client/pyscript.py @@ -7,8 +7,11 @@ command line. """ +from __future__ import annotations + import re import types +from typing import Any, Callable, Sequence import pythoncom import win32api @@ -291,7 +294,7 @@ def GetScriptDispatch(self, name): ) return self.scriptDispatch - def MakeEventMethodName(self, subItemName, eventName): + def MakeEventMethodName(self, subItemName: str, eventName: str): return ( subItemName[0].upper() + subItemName[1:] diff --git a/mypy.ini b/mypy.ini index a93198a2f..bfadecede 100644 --- a/mypy.ini +++ b/mypy.ini @@ -32,8 +32,24 @@ disable_error_code = misc, ; 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, + ; These are the other error codes that would currently fail with check_untyped_defs = true + ; 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, + ; type-var, + ; union-attr, + ; 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/win32/Lib/win32pdhquery.py b/win32/Lib/win32pdhquery.py index 3f458e0a8..f5900b45f 100644 --- a/win32/Lib/win32pdhquery.py +++ b/win32/Lib/win32pdhquery.py @@ -124,10 +124,12 @@ """ # Feb 12, 98 - MH added "rawaddcounter" so caller can get exception details. +from __future__ import annotations import _thread import copy import time +from itertools import chain import win32api import win32pdh @@ -446,14 +448,11 @@ def getinstpaths( cur += 1 except IndexError: # if we went over the end pass - paths = [] - for ind in range(len(temp)): - # can this raise an error? - paths.append( - win32pdh.MakeCounterPath( - (machine, "Process", object, None, ind, counter) - ) - ) + paths = [ + win32pdh.MakeCounterPath((machine, "Process", object, None, ind, counter)) + for ind in range(len(temp)) + ] + return paths # should also return the number of elements for naming purposes def open(self, *args, **namedargs): @@ -468,9 +467,11 @@ def open(self, *args, **namedargs): # do all the normal opening stuff, self._base is now the query object BaseQuery.open(*(self,) + args, **namedargs) # should rewrite getinstpaths to take a single tuple - paths = [] - for tup in self.volatilecounters: - paths[len(paths) :] = self.getinstpaths(*tup) + paths = list( + chain.from_iterable( + self.getinstpaths(*tup) for tup in self.volatilecounters + ) + ) for path in paths: try: self.counters.append(win32pdh.AddCounter(self._base, path)) diff --git a/win32/Lib/win32pdhutil.py b/win32/Lib/win32pdhutil.py index bc966412f..102ace27a 100644 --- a/win32/Lib/win32pdhutil.py +++ b/win32/Lib/win32pdhutil.py @@ -100,7 +100,7 @@ def FindPerformanceAttributesByName( instanceName = instanceName.lower() items, instances = win32pdh.EnumObjectItems(None, None, object, -1) # Track multiple instances. - instance_dict = {} + instance_dict: dict[str, int] = {} for instance in instances: try: instance_dict[instance] += 1 @@ -125,7 +125,7 @@ def ShowAllProcesses(): None, None, object, win32pdh.PERF_DETAIL_WIZARD ) # Need to track multiple instances of the same name. - instance_dict = {} + instance_dict: dict[str, int] = {} for instance in instances: try: instance_dict[instance] += 1 diff --git a/win32/Lib/win32serviceutil.py b/win32/Lib/win32serviceutil.py index 5601e13eb..363d2b17f 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 @@ -441,8 +442,8 @@ def ControlService(serviceName, code, machine=None): return status -def __FindSvcDeps(findName): - dict = {} +def __FindSvcDeps(findName: str): + deps_dict: dict[str, list[str]] = {} k = win32api.RegOpenKey( win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services" ) @@ -460,19 +461,19 @@ def __FindSvcDeps(findName): deps = () for dep in deps: dep = dep.lower() - dep_on = dict.get(dep, []) + dep_on = deps_dict.get(dep, []) dep_on.append(svc) - dict[dep] = dep_on + deps_dict[dep] = dep_on - return __ResolveDeps(findName, dict) + return __ResolveDeps(findName, deps_dict) -def __ResolveDeps(findName, dict): - items = dict.get(findName.lower(), []) - retList = [] +def __ResolveDeps(findName: str, deps_dict: dict[str, list[str]]): + items = deps_dict.get(findName.lower(), []) + retList: list[str] = [] for svc in items: retList.insert(0, svc) - retList = __ResolveDeps(svc, dict) + retList + retList = __ResolveDeps(svc, deps_dict) + retList return retList From 140050bc4c3c1ba278051499e812bae979f29331 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 27 Jul 2024 04:08:31 -0400 Subject: [PATCH 2/2] CI checkers --- Pythonwin/pywin/framework/intpyapp.py | 2 +- com/win32comext/axscript/client/pyscript.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Pythonwin/pywin/framework/intpyapp.py b/Pythonwin/pywin/framework/intpyapp.py index acbd34cc1..201cf95f0 100644 --- a/Pythonwin/pywin/framework/intpyapp.py +++ b/Pythonwin/pywin/framework/intpyapp.py @@ -29,7 +29,7 @@ def _SetupSharedMenu_(self): from pywin.mfc import docview -docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_ +docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_ # type: ignore[method-assign] class MainFrame(app.MainFrame): diff --git a/com/win32comext/axscript/client/pyscript.py b/com/win32comext/axscript/client/pyscript.py index 53744b8d2..17ac1e97b 100644 --- a/com/win32comext/axscript/client/pyscript.py +++ b/com/win32comext/axscript/client/pyscript.py @@ -11,7 +11,6 @@ import re import types -from typing import Any, Callable, Sequence import pythoncom import win32api