diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 260d16047b..a0583c3a9a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -95,13 +95,52 @@ jobs: path: | dist/*.whl + # This job can be run locally with the `format_all.bat` script checkers: - runs-on: ubuntu-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + # This job only needs to target the oldest supported version (black@stable supports Python >=3.8) + python-version: '3.8' - run: pip install isort pycln - run: pycln . --config=pycln.toml --check - run: isort . --diff --check-only - uses: psf/black@stable with: options: "--fast --check --diff --verbose" + + mypy: + runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + # 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: mypy . --python-version=${{ matrix.python-version }} + + pyright: + runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + python-version: ['3.7', '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 }} + # pyright vendors typeshed, but let's make sure we have the most up to date stubs + - run: pip install types-regex types-setuptools + - uses: jakebailey/pyright-action@v2 + with: + python-version: ${{ matrix.python-version }} + annotate: errors + diff --git a/CHANGES.txt b/CHANGES.txt index c31fd2dbe6..7a238523fb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -69,7 +69,7 @@ Build 305, released 2022-11-06 from there. (#1908) * Dropped support for allowing a bytes object to be passed where a COM BSTR - is expected - this support was accidental on the path from 2.x->3.x. + is expected - this support was accidental on the path from Python 2 -> 3. * win32crypt's PyCERTSTORE.CertCloseStore()'s `Flags` argument has been deprecated as it is likely to crash the process if diff --git a/Pythonwin/pywin/Demos/app/basictimerapp.py b/Pythonwin/pywin/Demos/app/basictimerapp.py index af2f95490b..a932ae0604 100644 --- a/Pythonwin/pywin/Demos/app/basictimerapp.py +++ b/Pythonwin/pywin/Demos/app/basictimerapp.py @@ -8,7 +8,7 @@ import win32api import win32con import win32ui -from pywin.framework import cmdline, dlgappcore +from pywin.framework import dlgappcore class TimerAppDialog(dlgappcore.AppDialog): diff --git a/Pythonwin/pywin/Demos/app/customprint.py b/Pythonwin/pywin/Demos/app/customprint.py index 356073e5fb..442737b5bd 100644 --- a/Pythonwin/pywin/Demos/app/customprint.py +++ b/Pythonwin/pywin/Demos/app/customprint.py @@ -5,7 +5,6 @@ # This sample was contributed by Roger Burnham. -import win32api import win32con import win32ui from pywin.framework import app diff --git a/Pythonwin/pywin/Demos/app/dlgappdemo.py b/Pythonwin/pywin/Demos/app/dlgappdemo.py index 38659a62fc..efb50a627e 100644 --- a/Pythonwin/pywin/Demos/app/dlgappdemo.py +++ b/Pythonwin/pywin/Demos/app/dlgappdemo.py @@ -9,7 +9,7 @@ import sys import win32ui -from pywin.framework import app, dlgappcore +from pywin.framework import dlgappcore class TestDialogApp(dlgappcore.DialogApp): @@ -43,7 +43,7 @@ def write(self, str): win32ui.OutputDebug("dlgapp - no edit control! >>\n%s\n<<\n" % str) -app.AppBuilder = TestDialogApp +app = TestDialogApp() if __name__ == "__main__": import demoutils diff --git a/Pythonwin/pywin/Demos/app/dojobapp.py b/Pythonwin/pywin/Demos/app/dojobapp.py index d97ce5e2a8..77197f4c25 100644 --- a/Pythonwin/pywin/Demos/app/dojobapp.py +++ b/Pythonwin/pywin/Demos/app/dojobapp.py @@ -6,10 +6,9 @@ # pythonwin /app demos\dojobapp.py -import win32api import win32con import win32ui -from pywin.framework import app, dlgappcore +from pywin.framework import dlgappcore class DoJobAppDialog(dlgappcore.AppDialog): @@ -57,7 +56,7 @@ def __init__(self): DoJobDialogApp.__init__(self) -app.AppBuilder = DoJobDialogApp +app = DoJobDialogApp() def t(): diff --git a/Pythonwin/pywin/Demos/guidemo.py b/Pythonwin/pywin/Demos/guidemo.py index 95b1dbbc91..887738c244 100644 --- a/Pythonwin/pywin/Demos/guidemo.py +++ b/Pythonwin/pywin/Demos/guidemo.py @@ -17,7 +17,7 @@ ("Dynamic window creation", "import createwin;createwin.demo()"), ("Various Dialog demos", "import dlgtest;dlgtest.demo()"), ("OCX Control Demo", "from ocx import ocxtest;ocxtest.demo()"), - ("OCX Serial Port Demo", "from ocx import ocxserialtest; ocxserialtest.test()"), + ("OCX Serial Port Demo", "from ocx import ocxserialtest; ocxserialtest.test()"), ( "IE4 Control Demo", 'from ocx import webbrowser; webbrowser.Demo("http://www.python.org")', diff --git a/Pythonwin/pywin/docking/DockingBar.py b/Pythonwin/pywin/docking/DockingBar.py index 34b14a41a1..3e9b4131a6 100644 --- a/Pythonwin/pywin/docking/DockingBar.py +++ b/Pythonwin/pywin/docking/DockingBar.py @@ -234,15 +234,15 @@ def OnWindowPosChanged(self, msg): return 0 lparam = msg[3] """ LPARAM used with WM_WINDOWPOSCHANGED: - typedef struct { - HWND hwnd; - HWND hwndInsertAfter; - int x; - int y; - int cx; - int cy; - UINT flags;} WINDOWPOS; - """ + typedef struct { + HWND hwnd; + HWND hwndInsertAfter; + int x; + int y; + int cx; + int cy; + UINT flags;} WINDOWPOS; + """ format = "PPiiiii" bytes = win32ui.GetBytes(lparam, struct.calcsize(format)) hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes) diff --git a/Pythonwin/pywin/framework/app.py b/Pythonwin/pywin/framework/app.py index 9793aa1307..96cf45368b 100644 --- a/Pythonwin/pywin/framework/app.py +++ b/Pythonwin/pywin/framework/app.py @@ -1,4 +1,3 @@ -# App.py # Application stuff. # The application is responsible for managing the main frame window. # @@ -17,29 +16,6 @@ from . import scriptutils -## NOTE: App and AppBuild should NOT be used - instead, you should contruct your -## APP class manually whenever you like (just ensure you leave these 2 params None!) -## Whoever wants the generic "Application" should get it via win32iu.GetApp() - -# These are "legacy" -AppBuilder = None -App = None # default - if used, must end up a CApp derived class. - - -# Helpers that should one day be removed! -def AddIdleHandler(handler): - print( - "app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead." - ) - return win32ui.GetApp().AddIdleHandler(handler) - - -def DeleteIdleHandler(handler): - print( - "app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead." - ) - return win32ui.GetApp().DeleteIdleHandler(handler) - # Helper for writing a Window position by name, and later loading it. def SaveWindowSize(section, rect, state=""): @@ -169,10 +145,6 @@ def ExitInstance(self): if self._obj_: self._obj_.AttachObject(None) self._obj_ = None - global App - global AppBuilder - App = None - AppBuilder = None return 0 def HaveIdleHandler(self, handler): diff --git a/Pythonwin/pywin/framework/bitmap.py b/Pythonwin/pywin/framework/bitmap.py index 0099fbe590..7619006f1f 100644 --- a/Pythonwin/pywin/framework/bitmap.py +++ b/Pythonwin/pywin/framework/bitmap.py @@ -128,7 +128,7 @@ def MatchDocType(self, fileName, fileType): # For debugging purposes, when this module may be reloaded many times. try: - win32ui.GetApp().RemoveDocTemplate(bitmapTemplate) + win32ui.GetApp().RemoveDocTemplate(bitmapTemplate) # type: ignore[has-type, used-before-def] except NameError: pass diff --git a/Pythonwin/pywin/framework/editor/color/coloreditor.py b/Pythonwin/pywin/framework/editor/color/coloreditor.py index 518e2a1528..6b42de93bf 100644 --- a/Pythonwin/pywin/framework/editor/color/coloreditor.py +++ b/Pythonwin/pywin/framework/editor/color/coloreditor.py @@ -9,7 +9,10 @@ from pywin.debugger import dbgcon from pywin.framework.editor import GetEditorOption from pywin.framework.editor.document import EditorDocumentBase +from pywin.framework.editor.frame import EditorFrame +from pywin.framework.editor.template import EditorTemplateBase from pywin.scintilla import bindings, scintillacon +from pywin.scintilla.view import CScintillaView as SyntEditViewParent # WARNING: Duplicated in document.py and editor.py MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER + 1999 @@ -36,9 +39,6 @@ def FinalizeViewCreation(self, view): self.GetDocTemplate().CheckIDLEMenus(view.idle) -SyntEditViewParent = pywin.scintilla.view.CScintillaView - - class SyntEditView(SyntEditViewParent): "A view of a SyntEdit. Obtains data from document." @@ -571,9 +571,6 @@ def FoldCollapseAllEvent(self, event): win32ui.DoWaitCursor(-1) -from pywin.framework.editor.frame import EditorFrame - - class SplitterFrame(EditorFrame): def OnCreate(self, cs): self.HookCommand(self.OnWindowSplit, win32ui.ID_WINDOW_SPLIT) @@ -584,9 +581,6 @@ def OnWindowSplit(self, id, code): return 1 -from pywin.framework.editor.template import EditorTemplateBase - - class SyntEditTemplate(EditorTemplateBase): def __init__( self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None @@ -644,7 +638,7 @@ def GetPythonPropertyPages(self): # For debugging purposes, when this module may be reloaded many times. try: - win32ui.GetApp().RemoveDocTemplate(editorTemplate) + win32ui.GetApp().RemoveDocTemplate(editorTemplate) # type: ignore[has-type, used-before-def] except NameError: pass diff --git a/Pythonwin/pywin/framework/editor/editor.py b/Pythonwin/pywin/framework/editor/editor.py index 6c332bd777..a1208cba6f 100644 --- a/Pythonwin/pywin/framework/editor/editor.py +++ b/Pythonwin/pywin/framework/editor/editor.py @@ -503,7 +503,7 @@ def Create(fileName=None, title=None, template=None): if __name__ == prefModule: # For debugging purposes, when this module may be reloaded many times. try: - win32ui.GetApp().RemoveDocTemplate(editorTemplate) + win32ui.GetApp().RemoveDocTemplate(editorTemplate) # type: ignore[has-type, used-before-def] except (NameError, win32ui.error): pass diff --git a/Pythonwin/pywin/framework/intpyapp.py b/Pythonwin/pywin/framework/intpyapp.py index e70e7c09f0..71690a9abe 100644 --- a/Pythonwin/pywin/framework/intpyapp.py +++ b/Pythonwin/pywin/framework/intpyapp.py @@ -551,8 +551,4 @@ def OnHelpIndex(self, id, code): help.SelectAndRunHelpFile() -# As per the comments in app.py, this use is depreciated. -# app.AppBuilder = InteractivePythonApp - -# Now all we do is create the application thisApp = InteractivePythonApp() diff --git a/Pythonwin/pywin/framework/mdi_pychecker.py b/Pythonwin/pywin/framework/mdi_pychecker.py index bde43f775b..6bd34905f9 100644 --- a/Pythonwin/pywin/framework/mdi_pychecker.py +++ b/Pythonwin/pywin/framework/mdi_pychecker.py @@ -842,7 +842,7 @@ def getNew(self): try: - win32ui.GetApp().RemoveDocTemplate(greptemplate) + win32ui.GetApp().RemoveDocTemplate(greptemplate) # type: ignore[has-type, used-before-def] except NameError: pass diff --git a/Pythonwin/pywin/framework/scriptutils.py b/Pythonwin/pywin/framework/scriptutils.py index a04ed7bbee..2f9c56e2de 100644 --- a/Pythonwin/pywin/framework/scriptutils.py +++ b/Pythonwin/pywin/framework/scriptutils.py @@ -1,6 +1,7 @@ """ Various utilities for running/importing a script """ + import bdb import linecache import os diff --git a/Pythonwin/pywin/framework/sgrepmdi.py b/Pythonwin/pywin/framework/sgrepmdi.py index a4068b5cd2..6383c939d5 100644 --- a/Pythonwin/pywin/framework/sgrepmdi.py +++ b/Pythonwin/pywin/framework/sgrepmdi.py @@ -751,7 +751,7 @@ def getNew(self): try: - win32ui.GetApp().RemoveDocTemplate(greptemplate) + win32ui.GetApp().RemoveDocTemplate(greptemplate) # type: ignore[has-type, used-before-def] except NameError: pass diff --git a/Pythonwin/pywin/framework/startup.py b/Pythonwin/pywin/framework/startup.py index 4a7323225d..5496063e38 100644 --- a/Pythonwin/pywin/framework/startup.py +++ b/Pythonwin/pywin/framework/startup.py @@ -64,17 +64,3 @@ # Import the application module. __import__(moduleName) - -try: - win32ui.GetApp()._obj_ - # This worked - an app already exists - do nothing more -except (AttributeError, win32ui.error): - # This means either no app object exists at all, or the one - # that does exist does not have a Python class (ie, was created - # by the host .EXE). In this case, we do the "old style" init... - from . import app - - if app.AppBuilder is None: - raise TypeError("No application object has been registered") - - app.App = app.AppBuilder() diff --git a/Pythonwin/pywin/framework/stdin.py b/Pythonwin/pywin/framework/stdin.py index 93f5c02df2..91fe7ef3e1 100644 --- a/Pythonwin/pywin/framework/stdin.py +++ b/Pythonwin/pywin/framework/stdin.py @@ -168,4 +168,4 @@ def fake_input(prompt=None): finally: get_input_line = input else: - sys.stdin = Stdin() + sys.stdin = Stdin() # type: ignore[assignment] # Not an actual TextIO diff --git a/Pythonwin/pywin/mfc/activex.py b/Pythonwin/pywin/mfc/activex.py index a9828e78dc..f3d9939a4b 100644 --- a/Pythonwin/pywin/mfc/activex.py +++ b/Pythonwin/pywin/mfc/activex.py @@ -1,5 +1,6 @@ """Support for ActiveX control hosting in Pythonwin. """ + import win32ui import win32uiole diff --git a/Pythonwin/pywin/mfc/dialog.py b/Pythonwin/pywin/mfc/dialog.py index 280c025aa1..41107e54d3 100644 --- a/Pythonwin/pywin/mfc/dialog.py +++ b/Pythonwin/pywin/mfc/dialog.py @@ -1,6 +1,7 @@ """ \ Base class for Dialogs. Also contains a few useful utility functions """ + # dialog.py # Python class for Dialog Boxes in PythonWin. diff --git a/Pythonwin/pywin/scintilla/IDLEenvironment.py b/Pythonwin/pywin/scintilla/IDLEenvironment.py index 126d6db9e5..9114c2c6bd 100644 --- a/Pythonwin/pywin/scintilla/IDLEenvironment.py +++ b/Pythonwin/pywin/scintilla/IDLEenvironment.py @@ -533,30 +533,30 @@ def test(): e.SetSel((4, 4)) skip = """ - TestCheck("insert", e, 4) - TestCheck("insert wordstart", e, 3) - TestCheck("insert wordend", e, 8) - TestCheck("insert linestart", e, 0) - TestCheck("insert lineend", e, 12) - TestCheck("insert + 4 chars", e, 8) - TestCheck("insert +4c", e, 8) - TestCheck("insert - 2 chars", e, 2) - TestCheck("insert -2c", e, 2) - TestCheck("insert-2c", e, 2) - TestCheck("insert-2 c", e, 2) - TestCheck("insert- 2c", e, 2) - TestCheck("1.1", e, 1) - TestCheck("1.0", e, 0) - TestCheck("2.0", e, 13) - try: - TestCheck("sel.first", e, 0) - print("*** sel.first worked with an empty selection") - except TextError: - pass - e.SetSel((4,5)) - TestCheck("sel.first- 2c", e, 2) - TestCheck("sel.last- 2c", e, 3) - """ + TestCheck("insert", e, 4) + TestCheck("insert wordstart", e, 3) + TestCheck("insert wordend", e, 8) + TestCheck("insert linestart", e, 0) + TestCheck("insert lineend", e, 12) + TestCheck("insert + 4 chars", e, 8) + TestCheck("insert +4c", e, 8) + TestCheck("insert - 2 chars", e, 2) + TestCheck("insert -2c", e, 2) + TestCheck("insert-2c", e, 2) + TestCheck("insert-2 c", e, 2) + TestCheck("insert- 2c", e, 2) + TestCheck("1.1", e, 1) + TestCheck("1.0", e, 0) + TestCheck("2.0", e, 13) + try: + TestCheck("sel.first", e, 0) + print("*** sel.first worked with an empty selection") + except TextError: + pass + e.SetSel((4,5)) + TestCheck("sel.first- 2c", e, 2) + TestCheck("sel.last- 2c", e, 3) + """ # Check EOL semantics e.SetSel((4, 4)) TestGet("insert lineend", "insert lineend +1c", t, "\n") diff --git a/Pythonwin/pywin/scintilla/bindings.py b/Pythonwin/pywin/scintilla/bindings.py index 44644bdfc8..16ed461a87 100644 --- a/Pythonwin/pywin/scintilla/bindings.py +++ b/Pythonwin/pywin/scintilla/bindings.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import traceback import win32api @@ -13,8 +15,8 @@ next_id = 5000 -event_to_commands = {} # dict of integer IDs to event names. -command_to_events = {} # dict of event names to int IDs +event_to_commands: dict[str, int] = {} # dict of event names to IDs +command_to_events: dict[int, str] = {} # dict of IDs to event names def assign_command_id(event, id=0): diff --git a/Pythonwin/pywin/scintilla/find.py b/Pythonwin/pywin/scintilla/find.py index e1d21a5bce..75fcaa16c7 100644 --- a/Pythonwin/pywin/scintilla/find.py +++ b/Pythonwin/pywin/scintilla/find.py @@ -1,4 +1,6 @@ # find.py - Find and Replace +from __future__ import annotations + import afxres import win32api import win32con @@ -35,7 +37,7 @@ def __setattr__(self, attr, val): curDialog = None lastSearch = defaultSearch = SearchParams() -searchHistory = [] +searchHistory: list[str] = [] def ShowFindDialog(): diff --git a/Pythonwin/pywin/tools/browseProjects.py b/Pythonwin/pywin/tools/browseProjects.py index 1a420b5f18..fe308c4f52 100644 --- a/Pythonwin/pywin/tools/browseProjects.py +++ b/Pythonwin/pywin/tools/browseProjects.py @@ -24,7 +24,7 @@ def GetText(self): class HLICLBRItem(hierlist.HierListItem): - def __init__(self, name, file, lineno, suffix=""): + def __init__(self, name: str, file, lineno, suffix=""): # If the 'name' object itself has a .name, use it. Not sure # how this happens, but seems pyclbr related. # See PyWin32 bug 817035 diff --git a/Pythonwin/pywin/tools/regpy.py b/Pythonwin/pywin/tools/regpy.py index 11ad63af56..2dfe228d81 100644 --- a/Pythonwin/pywin/tools/regpy.py +++ b/Pythonwin/pywin/tools/regpy.py @@ -1,8 +1,7 @@ # (sort-of) Registry editor import commctrl -import dialog import win32con -import win32ui +from pywin.mfc import dialog class RegistryControl: diff --git a/adodbapi/__init__.py b/adodbapi/__init__.py index 428bdf432a..94a04bee36 100644 --- a/adodbapi/__init__.py +++ b/adodbapi/__init__.py @@ -3,6 +3,7 @@ Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole * http://sourceforge.net/projects/adodbapi """ + import time # Re-exports to keep backward compatibility with existing code diff --git a/adodbapi/adodbapi.py b/adodbapi/adodbapi.py index 36a2c88961..1dcca42c1f 100644 --- a/adodbapi/adodbapi.py +++ b/adodbapi/adodbapi.py @@ -91,7 +91,7 @@ def getIndexedValue(obj, index): from collections.abc import Mapping -# --- define objects to smooth out Python3000 <-> Python 2.x differences +# --- define objects to smooth out Python3000 <-> Python 2 differences unicodeType = str longType = int StringTypes = str diff --git a/adodbapi/apibase.py b/adodbapi/apibase.py index d16265917f..19ae54814e 100644 --- a/adodbapi/apibase.py +++ b/adodbapi/apibase.py @@ -26,7 +26,7 @@ DateTime = type(NotImplemented) # should never be seen on win32 NullTypes = type(None) -# --- define objects to smooth out Python3 <-> Python 2.x differences +# --- define objects to smooth out Python3 <-> Python 2 differences unicodeType = str longType = int StringTypes = str @@ -55,7 +55,7 @@ def standardErrorHandler(connection, cursor, errorclass, errorvalue): raise errorclass(errorvalue) -# Note: _BaseException is defined differently between Python 2.x and 3.x +# Note: _BaseException is defined differently between Python 2 and 3 class Error(_BaseException): pass # Exception that is the base class of all other error # exceptions. You can use this to catch all errors with one diff --git a/adodbapi/examples/db_table_names.py b/adodbapi/examples/db_table_names.py index 2d7bf9df0d..907bdb85ea 100644 --- a/adodbapi/examples/db_table_names.py +++ b/adodbapi/examples/db_table_names.py @@ -1,4 +1,5 @@ """ db_table_names.py -- a simple demo for ADO database table listing.""" + import sys import adodbapi diff --git a/adodbapi/is64bit.py b/adodbapi/is64bit.py index 911c61931e..36a8c63e48 100644 --- a/adodbapi/is64bit.py +++ b/adodbapi/is64bit.py @@ -1,4 +1,5 @@ """is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version""" + import sys diff --git a/adodbapi/process_connect_string.py b/adodbapi/process_connect_string.py index 3c3f9e7afa..806c0954e8 100644 --- a/adodbapi/process_connect_string.py +++ b/adodbapi/process_connect_string.py @@ -1,4 +1,5 @@ """ a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" + from . import is64bit as is64bit diff --git a/adodbapi/remote.py b/adodbapi/remote.py index ae22b5a7ea..910c6b49a6 100644 --- a/adodbapi/remote.py +++ b/adodbapi/remote.py @@ -62,7 +62,7 @@ if verbose: print(version) -# --- define objects to smooth out Python3 <-> Python 2.x differences +# --- define objects to smooth out Python3 <-> Python 2 differences unicodeType = str # this line will be altered by 2to3.py to '= str' longType = int # this line will be altered by 2to3.py to '= int' StringTypes = str diff --git a/adodbapi/schema_table.py b/adodbapi/schema_table.py index 21ad37e31d..636766bba5 100644 --- a/adodbapi/schema_table.py +++ b/adodbapi/schema_table.py @@ -1,4 +1,5 @@ """call using an open ADO connection --> list of table names""" + from . import adodbapi diff --git a/adodbapi/setup.py b/adodbapi/setup.py index d25869adf9..51c999e5b4 100644 --- a/adodbapi/setup.py +++ b/adodbapi/setup.py @@ -3,6 +3,7 @@ Adodbapi can be run on CPython 3.5 and later. or IronPython version 2.6 and later (in theory, possibly no longer in practice!) """ + CLASSIFIERS = """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers diff --git a/adodbapi/test/adodbapitest.py b/adodbapi/test/adodbapitest.py index e27bcaede1..a5d4fce867 100644 --- a/adodbapi/test/adodbapitest.py +++ b/adodbapi/test/adodbapitest.py @@ -1,4 +1,5 @@ """ Unit tests version 2.6.1.0 for adodbapi""" + """ adodbapi - A python DB API 2.0 interface to Microsoft ADO @@ -251,9 +252,9 @@ def testUserDefinedConversionForExactNumericTypes(self): ) finally: # now reset the converter to its original function - adodbapi.variantConversions[ - ado_consts.adNumeric - ] = oldconverter # Restore the original convertion function + adodbapi.variantConversions[ado_consts.adNumeric] = ( + oldconverter # Restore the original convertion function + ) def helpTestDataType( self, diff --git a/adodbapi/test/dbapi20.py b/adodbapi/test/dbapi20.py index 5f983cdc8d..2cd8335776 100644 --- a/adodbapi/test/dbapi20.py +++ b/adodbapi/test/dbapi20.py @@ -18,13 +18,13 @@ import time import unittest -if sys.version[0] >= "3": # python 3.x +if sys.version[0] >= "3": # Python 3 _BaseException = Exception def _failUnless(self, expr, msg=None): self.assertTrue(expr, msg) -else: # python 2.x +else: # Python 2 from exceptions import Exception as _BaseException def _failUnless(self, expr, msg=None): diff --git a/adodbapi/test/is64bit.py b/adodbapi/test/is64bit.py index ed390fad00..33d0244302 100644 --- a/adodbapi/test/is64bit.py +++ b/adodbapi/test/is64bit.py @@ -1,4 +1,5 @@ """is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version""" + import sys diff --git a/adodbapi/test/test_adodbapi_dbapi20.py b/adodbapi/test/test_adodbapi_dbapi20.py index b90844bf6a..ccf7e7cbf5 100644 --- a/adodbapi/test/test_adodbapi_dbapi20.py +++ b/adodbapi/test/test_adodbapi_dbapi20.py @@ -51,9 +51,9 @@ else: conn_kws["host"] = host -conn_kws[ - "provider" -] = "Provider=MSOLEDBSQL;DataTypeCompatibility=80;MARS Connection=True;" +conn_kws["provider"] = ( + "Provider=MSOLEDBSQL;DataTypeCompatibility=80;MARS Connection=True;" +) connStr = "%(provider)s; %(security)s; Initial Catalog=%(name)s;Data Source=%(host)s" if onWindows and node != "z-PC": diff --git a/com/pythoncom.py b/com/pythoncom.py index 2180ecc32e..ef68dc6666 100644 --- a/com/pythoncom.py +++ b/com/pythoncom.py @@ -1,4 +1,4 @@ -# Magic utility that "redirects" to pythoncomxx.dll +# Magic utility that "redirects" to pythoncomXX.dll import pywintypes pywintypes.__import_pywin32_system_module__("pythoncom", globals()) diff --git a/com/win32com/client/CLSIDToClass.py b/com/win32com/client/CLSIDToClass.py index fde3274332..88b742e0ac 100644 --- a/com/win32com/client/CLSIDToClass.py +++ b/com/win32com/client/CLSIDToClass.py @@ -16,6 +16,7 @@ This obviously applies to all cooperating objects, not just DAO and Access. """ + mapCLSIDToClass = {} diff --git a/com/win32com/client/__init__.py b/com/win32com/client/__init__.py index 36f2c1d778..4cc903d506 100644 --- a/com/win32com/client/__init__.py +++ b/com/win32com/client/__init__.py @@ -21,7 +21,6 @@ def __WrapDispatch( userName=None, resultCLSID=None, typeinfo=None, - UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER, WrapperClass=None, ): @@ -29,7 +28,6 @@ def __WrapDispatch( Helper function to return a makepy generated class for a CLSID if it exists, otherwise cope by using CDispatch. """ - assert UnicodeToString is None, "this is deprecated and will go away" if resultCLSID is None: try: typeinfo = dispatch.GetTypeInfo() @@ -110,11 +108,9 @@ def Dispatch( userName=None, resultCLSID=None, typeinfo=None, - UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER, ): """Creates a Dispatch based COM object.""" - assert UnicodeToString is None, "this is deprecated and will go away" dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch, userName, clsctx) return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx) @@ -125,11 +121,9 @@ def DispatchEx( userName=None, resultCLSID=None, typeinfo=None, - UnicodeToString=None, clsctx=None, ): """Creates a Dispatch based COM object on a specific machine.""" - assert UnicodeToString is None, "this is deprecated and will go away" # If InProc is registered, DCOM will use it regardless of the machine name # (and regardless of the DCOM config for the object.) So unless the user # specifies otherwise, we exclude inproc apps when a remote machine is used. @@ -157,11 +151,8 @@ class CDispatch(dynamic.CDispatch): if/when possible. """ - def _wrap_dispatch_( - self, ob, userName=None, returnCLSID=None, UnicodeToString=None - ): - assert UnicodeToString is None, "this is deprecated and will go away" - return Dispatch(ob, userName, returnCLSID, None) + def _wrap_dispatch_(self, ob, userName=None, returnCLSID=None): + return Dispatch(ob, userName, returnCLSID) def __dir__(self): return dynamic.CDispatch.__dir__(self) diff --git a/com/win32com/client/build.py b/com/win32com/client/build.py index 1035ecdff9..8318b88949 100644 --- a/com/win32com/client/build.py +++ b/com/win32com/client/build.py @@ -69,9 +69,7 @@ class NotSupportedException(Exception): pythoncom.VT_VOID, ] -NoTranslateMap = {} -for v in NoTranslateTypes: - NoTranslateMap[v] = None +NoTranslateMap = set(NoTranslateTypes) class MapEntry: diff --git a/com/win32com/client/combrowse.py b/com/win32com/client/combrowse.py index 18576251ee..a5e76a85ba 100644 --- a/com/win32com/client/combrowse.py +++ b/com/win32com/client/combrowse.py @@ -22,6 +22,7 @@ work. """ + import sys import pythoncom diff --git a/com/win32com/client/connect.py b/com/win32com/client/connect.py index 24788204ec..7cb3969464 100644 --- a/com/win32com/client/connect.py +++ b/com/win32com/client/connect.py @@ -1,4 +1,5 @@ """Utilities for working with Connections""" + import pythoncom import win32com.server.util diff --git a/com/win32com/client/dynamic.py b/com/win32com/client/dynamic.py index 4c38ba3b1b..18dd751bbb 100644 --- a/com/win32com/client/dynamic.py +++ b/com/win32com/client/dynamic.py @@ -15,6 +15,7 @@ >>> xl.Visible = 1 # The Excel window becomes visible. """ + import traceback import types @@ -125,10 +126,8 @@ def Dispatch( userName=None, createClass=None, typeinfo=None, - UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER, ): - assert UnicodeToString is None, "this is deprecated and will go away" IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch, userName, clsctx) if createClass is None: createClass = CDispatch @@ -180,11 +179,9 @@ def DumbDispatch( IDispatch, userName=None, createClass=None, - UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER, ): "Dispatch with no type info" - assert UnicodeToString is None, "this is deprecated and will go away" IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch, userName, clsctx) if createClass is None: createClass = CDispatch @@ -192,10 +189,7 @@ def DumbDispatch( class CDispatch: - def __init__( - self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None - ): - assert UnicodeToString is None, "this is deprecated and will go away" + def __init__(self, IDispatch, olerepr, userName=None, lazydata=None): if userName is None: userName = "" self.__dict__["_oleobj_"] = IDispatch @@ -369,10 +363,12 @@ def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *ar return self._get_good_object_(result, user, resultCLSID) def _wrap_dispatch_( - self, ob, userName=None, returnCLSID=None, UnicodeToString=None + self, + ob, + userName=None, + returnCLSID=None, ): # Given a dispatch object, wrap it in a class - assert UnicodeToString is None, "this is deprecated and will go away" return Dispatch(ob, userName) def _get_good_single_object_(self, ob, userName=None, ReturnCLSID=None): diff --git a/com/win32com/client/gencache.py b/com/win32com/client/gencache.py index c7bbbca3cb..296e594e84 100644 --- a/com/win32com/client/gencache.py +++ b/com/win32com/client/gencache.py @@ -20,10 +20,15 @@ Currently just uses a pickled dictionary, but should used some sort of indexed file. Maybe an OLE2 compound file, or a bsddb file? """ + +from __future__ import annotations + import glob import os import sys from importlib import reload +from types import ModuleType +from typing import Any import pythoncom import pywintypes @@ -35,11 +40,11 @@ bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also makepy.py # The global dictionary -clsidToTypelib = {} +clsidToTypelib: dict[str, tuple[str, int, int, int]] = {} # If we have a different version of the typelib generated, this # maps the "requested version" to the "generated version". -versionRedirectMap = {} +versionRedirectMap: dict[tuple[str, int, int, int], ModuleType | None] = {} # There is no reason we *must* be readonly in a .zip, but we are now, # Rather than check for ".zip" or other tricks, PEP302 defines @@ -52,7 +57,8 @@ # A dictionary of ITypeLibrary objects for demand generation explicitly handed to us # Keyed by usual clsid, lcid, major, minor -demandGeneratedTypeLibraries = {} +# Typing as Any because PyITypeLib is not exposed +demandGeneratedTypeLibraries: dict[tuple[str, int, int, int], Any] = {} import pickle as pickle @@ -768,12 +774,12 @@ def _Dump(): def usage(): usageString = """\ - Usage: gencache [-q] [-d] [-r] + Usage: gencache [-q] [-d] [-r] - -q - Quiet - -d - Dump the cache (typelibrary description and filename). - -r - Rebuild the cache dictionary from the existing .py files - """ + -q - Quiet + -d - Dump the cache (typelibrary description and filename). + -r - Rebuild the cache dictionary from the existing .py files + """ print(usageString) sys.exit(1) diff --git a/com/win32com/client/genpy.py b/com/win32com/client/genpy.py index d23cd3079f..a45791ae35 100644 --- a/com/win32com/client/genpy.py +++ b/com/win32com/client/genpy.py @@ -877,9 +877,7 @@ def __init__( sourceFilename, progressObject, bBuildHidden=1, - bUnicodeToString=None, ): - assert bUnicodeToString is None, "this is deprecated and will go away" self.bHaveWrittenDispatchBaseClass = 0 self.bHaveWrittenCoClassBaseClass = 0 self.bHaveWrittenEventBaseClass = 0 diff --git a/com/win32com/client/makepy.py b/com/win32com/client/makepy.py index 5bcea6c830..53ff7e1c71 100644 --- a/com/win32com/client/makepy.py +++ b/com/win32com/client/makepy.py @@ -240,11 +240,9 @@ def GenerateFromTypeLibSpec( file=None, verboseLevel=None, progressInstance=None, - bUnicodeToString=None, bForDemand=bForDemandDefault, bBuildHidden=1, ): - assert bUnicodeToString is None, "this is deprecated and will go away" if verboseLevel is None: verboseLevel = 0 # By default, we use no gui and no verbose level! @@ -338,9 +336,8 @@ def GenerateFromTypeLibSpec( def GenerateChildFromTypeLibSpec( - child, typelibInfo, verboseLevel=None, progressInstance=None, bUnicodeToString=None + child, typelibInfo, verboseLevel=None, progressInstance=None ): - assert bUnicodeToString is None, "this is deprecated and will go away" if verboseLevel is None: verboseLevel = ( 0 # By default, we use no gui, and no verbose level for the children. diff --git a/com/win32com/client/util.py b/com/win32com/client/util.py index 1925843ab3..33f5763367 100644 --- a/com/win32com/client/util.py +++ b/com/win32com/client/util.py @@ -3,6 +3,7 @@ This module contains utility functions, used primarily by advanced COM programmers, or other COM modules. """ + import pythoncom from win32com.client import Dispatch, _get_good_object_ diff --git a/com/win32com/demos/excelAddin.py b/com/win32com/demos/excelAddin.py index 8229477a8c..18a76eb8be 100644 --- a/com/win32com/demos/excelAddin.py +++ b/com/win32com/demos/excelAddin.py @@ -51,7 +51,6 @@ import pythoncom from win32com import universal from win32com.client import Dispatch, DispatchWithEvents, constants, gencache -from win32com.server.exception import COMException # Support for COM objects we use. gencache.EnsureModule( diff --git a/com/win32com/demos/outlookAddin.py b/com/win32com/demos/outlookAddin.py index 58f2f69878..244d5b325e 100644 --- a/com/win32com/demos/outlookAddin.py +++ b/com/win32com/demos/outlookAddin.py @@ -30,7 +30,6 @@ import pythoncom from win32com import universal from win32com.client import DispatchWithEvents, constants, gencache -from win32com.server.exception import COMException # Support for COM objects we use. gencache.EnsureModule( diff --git a/com/win32com/demos/trybag.py b/com/win32com/demos/trybag.py index 3d3918b6c2..07f0672fcb 100644 --- a/com/win32com/demos/trybag.py +++ b/com/win32com/demos/trybag.py @@ -1,5 +1,6 @@ import pythoncom -from win32com.server import exception, util +from win32com.server import util +from win32com.server.exception import COMException VT_EMPTY = pythoncom.VT_EMPTY @@ -18,7 +19,7 @@ def Read(self, propName, varType, errorLog): hr = 0x80070057 exc = pythoncom.com_error(0, "Bag.Read", "no such item", None, 0, hr) errorLog.AddError(propName, exc) - raise exception.Exception(scode=hr) + raise COMException(scode=hr) return self.data[propName] def Write(self, propName, value): @@ -31,7 +32,7 @@ class Target: _com_interfaces_ = [pythoncom.IID_IPersist, pythoncom.IID_IPersistPropertyBag] def GetClassID(self): - raise exception.Exception(scode=0x80004005) # E_FAIL + raise COMException(scode=0x80004005) # E_FAIL def InitNew(self): pass @@ -41,7 +42,7 @@ def Load(self, bag, log): print(bag.Read("prop2", VT_EMPTY, log)) try: print(bag.Read("prop3", VT_EMPTY, log)) - except exception.Exception: + except COMException: pass def Save(self, bag, clearDirty, saveAllProps): diff --git a/com/win32com/makegw/makegw.py b/com/win32com/makegw/makegw.py index 196fce4276..d99fa540a2 100644 --- a/com/win32com/makegw/makegw.py +++ b/com/win32com/makegw/makegw.py @@ -3,9 +3,9 @@ This module will generate a C++/Python binding for a specific COM interface. - At this stage, no command line interface exists. You must start Python, - import this module, change to the directory where the generated code should - be written, and run the public function. + Can be run from command line (passing required arguments) or the old way + (start Python, import this module, change to the directory where the generated code + should be written, and run the public function). This module is capable of generating both 'Interfaces' (ie, Python client side support for the interface) and 'Gateways' (ie, Python @@ -46,13 +46,19 @@ """ +import argparse +import os import re from . import makegwparse def make_framework_support( - header_file_name, interface_name, bMakeInterface=1, bMakeGateway=1 + header_file_name, + interface_name, + bMakeInterface=1, + bMakeGateway=1, + output_directory=None, ): """Generate C++ code for a Python Interface and Gateway @@ -86,7 +92,12 @@ def make_framework_support( ifc_cpp_writer = _write_ifc_cpp gw_cpp_writer = _write_gw_cpp - fout = open("Py%s.cpp" % interface.name, "w") + fout = open( + os.path.join( + directory if directory else os.getcwd(), f"Py{interface.name}.cpp" + ), + "w", + ) try: fout.write( f"""\ @@ -110,7 +121,10 @@ def make_framework_support( gw_cpp_writer(fout, interface) finally: fout.close() - fout = open("Py%s.h" % interface.name, "w") + fout = open( + os.path.join(directory if directory else os.getcwd(), f"Py{interface.name}.h"), + "w", + ) try: fout.write( f"""\ @@ -144,11 +158,11 @@ def _write_ifc_h(f, interface): class Py{interface.name} : public Py{interface.base} {{ public: - MAKE_PYCOM_CTOR(Py{interface.name}); - static {interface.name} *GetI(PyObject *self); - static PyComTypeObject type; + MAKE_PYCOM_CTOR(Py{interface.name}); + static {interface.name} *GetI(PyObject *self); + static PyComTypeObject type; - // The Python methods + // The Python methods """ ) for method in interface.methods: @@ -159,8 +173,8 @@ class Py{interface.name} : public Py{interface.base} f"""\ protected: - Py{interface.name}(IUnknown *pdisp); - ~Py{interface.name}(); + Py{interface.name}(IUnknown *pdisp); + ~Py{interface.name}(); }}; """ ) @@ -175,9 +189,9 @@ def _write_ifc_cpp(f, interface): // Interface Implementation Py{name}::Py{name}(IUnknown *pdisp): - Py{base}(pdisp) + Py{base}(pdisp) {{ - ob_type = &type; + ob_type = &type; }} Py{name}::~Py{name}() @@ -186,7 +200,7 @@ def _write_ifc_cpp(f, interface): /* static */ {name} *Py{name}::GetI(PyObject *self) {{ - return ({name} *)Py{base}::GetI(self); + return ({name} *)Py{base}::GetI(self); }} """.format( @@ -203,18 +217,16 @@ def _write_ifc_cpp(f, interface): // @pymethod |Py{interfacename}|{method}|Description of {method}. PyObject *Py{interfacename}::{method}(PyObject *self, PyObject *args) {{ - {interfacename} *p{ptr} = GetI(self); - if ( p{ptr} == NULL ) - return NULL; + {interfacename} *p{ptr} = GetI(self); + if ( p{ptr} == NULL ) + return NULL; """.format( **strdict ) ) - argsParseTuple = ( - argsCOM - ) = ( - formatChars - ) = codePost = codePobjects = codeCobjects = cleanup = cleanup_gil = "" + argsParseTuple = argsCOM = formatChars = codePost = codePobjects = ( + codeCobjects + ) = cleanup = cleanup_gil = "" needConversion = 0 # if method.name=="Stat": import win32dbg;win32dbg.brk() for arg in method.args: @@ -282,14 +294,14 @@ def _write_ifc_cpp(f, interface): strdict["cleanup"] = cleanup strdict["cleanup_gil"] = cleanup_gil f.write( - """ HRESULT hr; - PY_INTERFACE_PRECALL; - hr = p{ptr}->{method}({argsCOM} ); + """ HRESULT hr; + PY_INTERFACE_PRECALL; + hr = p{ptr}->{method}({argsCOM} ); {cleanup} - PY_INTERFACE_POSTCALL; + PY_INTERFACE_POSTCALL; {cleanup_gil} - if ( FAILED(hr) ) - return PyCom_BuildPyException(hr, p{ptr}, IID_{interfacename} ); + if ( FAILED(hr) ) + return PyCom_BuildPyException(hr, p{ptr}, IID_{interfacename} ); """.format( **strdict ) @@ -321,7 +333,7 @@ def _write_ifc_cpp(f, interface): ) ) else: - f.write("\tPy_INCREF(Py_None);\n\treturn Py_None;\n") + f.write("\tPy_RETURN_NONE;\n") f.write("\n}\n\n") f.write("// @object Py%s|Description of the interface\n" % (name)) @@ -336,14 +348,14 @@ def _write_ifc_cpp(f, interface): interfacebase = interface.base f.write( """\ - {{ NULL }} + {{ NULL }} }}; PyComTypeObject Py{name}::type("Py{name}", - &Py{interfacebase}::type, - sizeof(Py{name}), - Py{name}_methods, - GET_PYCOM_CTOR(Py{name})); + &Py{interfacebase}::type, + sizeof(Py{name}), + Py{name}_methods, + GET_PYCOM_CTOR(Py{name})); """.format( **locals() ) @@ -372,8 +384,8 @@ def _write_gw_h(f, interface): class {gname} : public {base_name}, public {name} {{ protected: - {gname}(PyObject *instance) : {base_name}(instance) {{ ; }} - PYGATEWAY_MAKE_SUPPORT2({gname}, {name}, IID_{name}, {base_name}) + {gname}(PyObject *instance) : {base_name}(instance) {{ ; }} + PYGATEWAY_MAKE_SUPPORT2({gname}, {name}, IID_{name}, {base_name}) """ ) @@ -420,9 +432,7 @@ def _write_gw_cpp(f, interface): // --------------------------------------------------- // // Gateway Implementation -""".format( - name=name, gname=gname, base_name=base_name - ) +""" ) for method in interface.methods: @@ -568,3 +578,63 @@ def test(): # make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG") +# python -m com.win32com.makegw.makegw -f "C:\Windows Kits\10\Include\10.0.19041.0\um\ShObjIdl_core.h" -n IFolderView1 -o com\win32comext\shell\src + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="COM interface wrapper generator") + parser.add_argument( + "--header_file", "-f", required=True, help="header file (system) to parse" + ) + parser.add_argument( + "--interface_name", "-n", required=True, help="interface name to search for" + ) + parser.add_argument( + "--no_create_interface", + "-i", + action="store_false", + dest="create_interface", + help="do not generate interface code", + ) + parser.add_argument( + "--no_create_gateway", + "-g", + action="store_false", + dest="create_gateway", + help="do not generate gateway code", + ) + parser.add_argument( + "--output_directory", "-o", help="directory where to generate files" + ) + + args, unk = parser.parse_known_args() + if unk: + print(f"Warning: Ignoring unknown arguments: {unk}") + + if not args.header_file or not os.path.isfile(args.header_file): + parser.exit(status=-1, message="Invalid header file\n") + if not args.interface_name: + parser.exit(status=-1, message="Invalid interface name\n") + + return ( + args.header_file, + args.interface_name, + args.create_interface, + args.create_gateway, + args.output_directory or None, + ) + + +if __name__ == "__main__": + header_file, interface_name, create_interface, create_gateway, directory = ( + parse_arguments() + ) + if directory: + os.makedirs(directory, exist_ok=True) + make_framework_support( + header_file_name=header_file, + interface_name=interface_name, + bMakeInterface=create_interface, + bMakeGateway=create_gateway, + output_directory=directory, + ) diff --git a/com/win32com/makegw/makegwenum.py b/com/win32com/makegw/makegwenum.py index d541307d12..662dff4b67 100644 --- a/com/win32com/makegw/makegwenum.py +++ b/com/win32com/makegw/makegwenum.py @@ -7,6 +7,7 @@ This module is notmally not used directly - the @makegw@ module automatically calls this. """ + # # INTERNAL FUNCTIONS # @@ -44,9 +45,9 @@ def _write_enumifc_cpp(f, interface): // Interface Implementation PyIEnum{enumtype}::PyIEnum{enumtype}(IUnknown *pdisp): - PyIUnknown(pdisp) + PyIUnknown(pdisp) {{ - ob_type = &type; + ob_type = &type; }} PyIEnum{enumtype}::~PyIEnum{enumtype}() @@ -55,141 +56,141 @@ def _write_enumifc_cpp(f, interface): /* static */ IEnum{enumtype} *PyIEnum{enumtype}::GetI(PyObject *self) {{ - return (IEnum{enumtype} *)PyIUnknown::GetI(self); + return (IEnum{enumtype} *)PyIUnknown::GetI(self); }} // @pymethod object|PyIEnum{enumtype}|Next|Retrieves a specified number of items in the enumeration sequence. PyObject *PyIEnum{enumtype}::Next(PyObject *self, PyObject *args) {{ - long celt = 1; - // @pyparm int|num|1|Number of items to retrieve. - if ( !PyArg_ParseTuple(args, "|l:Next", &celt) ) - return NULL; - - IEnum{enumtype} *pIE{enumtype} = GetI(self); - if ( pIE{enumtype} == NULL ) - return NULL; - - {arraydeclare} - if ( rgVar == NULL ) {{ - PyErr_SetString(PyExc_MemoryError, "allocating result {enumtype}s"); - return NULL; - }} - - int i; -/* for ( i = celt; i--; ) - // *** possibly init each structure element??? + long celt = 1; + // @pyparm int|num|1|Number of items to retrieve. + if ( !PyArg_ParseTuple(args, "|l:Next", &celt) ) + return NULL; + + IEnum{enumtype} *pIE{enumtype} = GetI(self); + if ( pIE{enumtype} == NULL ) + return NULL; + + {arraydeclare} + if ( rgVar == NULL ) {{ + PyErr_SetString(PyExc_MemoryError, "allocating result {enumtype}s"); + return NULL; + }} + + int i; +/* for ( i = celt; i--; ) + // *** possibly init each structure element??? */ - ULONG celtFetched = 0; - PY_INTERFACE_PRECALL; - HRESULT hr = pIE{enumtype}->Next(celt, rgVar, &celtFetched); - PY_INTERFACE_POSTCALL; - if ( HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS && FAILED(hr) ) - {{ - delete [] rgVar; - return PyCom_BuildPyException(hr,pIE{enumtype}, IID_IE{enumtype}); - }} - - PyObject *result = PyTuple_New(celtFetched); - if ( result != NULL ) - {{ - for ( i = celtFetched; i--; ) - {{ - {converter} - if ( ob == NULL ) - {{ - Py_DECREF(result); - result = NULL; - break; - }} - PyTuple_SET_ITEM(result, i, ob); - }} - }} - -/* for ( i = celtFetched; i--; ) - // *** possibly cleanup each structure element??? + ULONG celtFetched = 0; + PY_INTERFACE_PRECALL; + HRESULT hr = pIE{enumtype}->Next(celt, rgVar, &celtFetched); + PY_INTERFACE_POSTCALL; + if ( HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS && FAILED(hr) ) + {{ + delete [] rgVar; + return PyCom_BuildPyException(hr,pIE{enumtype}, IID_IE{enumtype}); + }} + + PyObject *result = PyTuple_New(celtFetched); + if ( result != NULL ) + {{ + for ( i = celtFetched; i--; ) + {{ + {converter} + if ( ob == NULL ) + {{ + Py_DECREF(result); + result = NULL; + break; + }} + PyTuple_SET_ITEM(result, i, ob); + }} + }} + +/* for ( i = celtFetched; i--; ) + // *** possibly cleanup each structure element??? */ - delete [] rgVar; - return result; + delete [] rgVar; + return result; }} // @pymethod |PyIEnum{enumtype}|Skip|Skips over the next specified elementes. PyObject *PyIEnum{enumtype}::Skip(PyObject *self, PyObject *args) {{ - long celt; - if ( !PyArg_ParseTuple(args, "l:Skip", &celt) ) - return NULL; - - IEnum{enumtype} *pIE{enumtype} = GetI(self); - if ( pIE{enumtype} == NULL ) - return NULL; - - PY_INTERFACE_PRECALL; - HRESULT hr = pIE{enumtype}->Skip(celt); - PY_INTERFACE_POSTCALL; - if ( FAILED(hr) ) - return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); - - Py_INCREF(Py_None); - return Py_None; + long celt; + if ( !PyArg_ParseTuple(args, "l:Skip", &celt) ) + return NULL; + + IEnum{enumtype} *pIE{enumtype} = GetI(self); + if ( pIE{enumtype} == NULL ) + return NULL; + + PY_INTERFACE_PRECALL; + HRESULT hr = pIE{enumtype}->Skip(celt); + PY_INTERFACE_POSTCALL; + if ( FAILED(hr) ) + return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); + + Py_INCREF(Py_None); + return Py_None; }} // @pymethod |PyIEnum{enumtype}|Reset|Resets the enumeration sequence to the beginning. PyObject *PyIEnum{enumtype}::Reset(PyObject *self, PyObject *args) {{ - if ( !PyArg_ParseTuple(args, ":Reset") ) - return NULL; + if ( !PyArg_ParseTuple(args, ":Reset") ) + return NULL; - IEnum{enumtype} *pIE{enumtype} = GetI(self); - if ( pIE{enumtype} == NULL ) - return NULL; + IEnum{enumtype} *pIE{enumtype} = GetI(self); + if ( pIE{enumtype} == NULL ) + return NULL; - PY_INTERFACE_PRECALL; - HRESULT hr = pIE{enumtype}->Reset(); - PY_INTERFACE_POSTCALL; - if ( FAILED(hr) ) - return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); + PY_INTERFACE_PRECALL; + HRESULT hr = pIE{enumtype}->Reset(); + PY_INTERFACE_POSTCALL; + if ( FAILED(hr) ) + return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(Py_None); + return Py_None; }} // @pymethod |PyIEnum{enumtype}|Clone|Creates another enumerator that contains the same enumeration state as the current one PyObject *PyIEnum{enumtype}::Clone(PyObject *self, PyObject *args) {{ - if ( !PyArg_ParseTuple(args, ":Clone") ) - return NULL; + if ( !PyArg_ParseTuple(args, ":Clone") ) + return NULL; - IEnum{enumtype} *pIE{enumtype} = GetI(self); - if ( pIE{enumtype} == NULL ) - return NULL; + IEnum{enumtype} *pIE{enumtype} = GetI(self); + if ( pIE{enumtype} == NULL ) + return NULL; - IEnum{enumtype} *pClone; - PY_INTERFACE_PRECALL; - HRESULT hr = pIE{enumtype}->Clone(&pClone); - PY_INTERFACE_POSTCALL; - if ( FAILED(hr) ) - return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); + IEnum{enumtype} *pClone; + PY_INTERFACE_PRECALL; + HRESULT hr = pIE{enumtype}->Clone(&pClone); + PY_INTERFACE_POSTCALL; + if ( FAILED(hr) ) + return PyCom_BuildPyException(hr, pIE{enumtype}, IID_IE{enumtype}); - return PyCom_PyObjectFromIUnknown(pClone, IID_IEnum{enumtype}, FALSE); + return PyCom_PyObjectFromIUnknown(pClone, IID_IEnum{enumtype}, FALSE); }} // @object PyIEnum{enumtype}|A Python interface to IEnum{enumtype} static struct PyMethodDef PyIEnum{enumtype}_methods[] = {{ - {{ "Next", PyIEnum{enumtype}::Next, 1 }}, // @pymeth Next|Retrieves a specified number of items in the enumeration sequence. - {{ "Skip", PyIEnum{enumtype}::Skip, 1 }}, // @pymeth Skip|Skips over the next specified elementes. - {{ "Reset", PyIEnum{enumtype}::Reset, 1 }}, // @pymeth Reset|Resets the enumeration sequence to the beginning. - {{ "Clone", PyIEnum{enumtype}::Clone, 1 }}, // @pymeth Clone|Creates another enumerator that contains the same enumeration state as the current one. - {{ NULL }} + {{ "Next", PyIEnum{enumtype}::Next, 1 }}, // @pymeth Next|Retrieves a specified number of items in the enumeration sequence. + {{ "Skip", PyIEnum{enumtype}::Skip, 1 }}, // @pymeth Skip|Skips over the next specified elementes. + {{ "Reset", PyIEnum{enumtype}::Reset, 1 }}, // @pymeth Reset|Resets the enumeration sequence to the beginning. + {{ "Clone", PyIEnum{enumtype}::Clone, 1 }}, // @pymeth Clone|Creates another enumerator that contains the same enumeration state as the current one. + {{ NULL }} }}; PyComEnumTypeObject PyIEnum{enumtype}::type("PyIEnum{enumtype}", - &PyIUnknown::type, - sizeof(PyIEnum{enumtype}), - PyIEnum{enumtype}_methods, - GET_PYCOM_CTOR(PyIEnum{enumtype})); + &PyIUnknown::type, + sizeof(PyIEnum{enumtype}), + PyIEnum{enumtype}_methods, + GET_PYCOM_CTOR(PyIEnum{enumtype})); """.format( **locals() ) @@ -230,103 +231,103 @@ def _write_enumgw_cpp(f, interface): /* [length_is][size_is][out] */ {argdeclare}, /* [out] */ ULONG __RPC_FAR *pCeltFetched) {{ - PY_GATEWAY_METHOD; - PyObject *result; - HRESULT hr = InvokeViaPolicy("Next", &result, "i", celt); - if ( FAILED(hr) ) - return hr; - - if ( !PySequence_Check(result) ) - goto error; - int len; - len = PyObject_Length(result); - if ( len == -1 ) - goto error; - if ( len > (int)celt) - len = celt; - - if ( pCeltFetched ) - *pCeltFetched = len; - - int i; - for ( i = 0; i < len; ++i ) - {{ - PyObject *ob = PySequence_GetItem(result, i); - if ( ob == NULL ) - goto error; - - {converter} - {{ - Py_DECREF(result); - return PyCom_SetCOMErrorFromPyException(IID_IEnum{enumtype}); - }} - }} - - Py_DECREF(result); - - return len < (int)celt ? S_FALSE : S_OK; + PY_GATEWAY_METHOD; + PyObject *result; + HRESULT hr = InvokeViaPolicy("Next", &result, "i", celt); + if ( FAILED(hr) ) + return hr; + + if ( !PySequence_Check(result) ) + goto error; + int len; + len = PyObject_Length(result); + if ( len == -1 ) + goto error; + if ( len > (int)celt) + len = celt; + + if ( pCeltFetched ) + *pCeltFetched = len; + + int i; + for ( i = 0; i < len; ++i ) + {{ + PyObject *ob = PySequence_GetItem(result, i); + if ( ob == NULL ) + goto error; + + {converter} + {{ + Py_DECREF(result); + return PyCom_SetCOMErrorFromPyException(IID_IEnum{enumtype}); + }} + }} + + Py_DECREF(result); + + return len < (int)celt ? S_FALSE : S_OK; error: - PyErr_Clear(); // just in case - Py_DECREF(result); - return PyCom_HandleIEnumNoSequence(IID_IEnum{enumtype}); + PyErr_Clear(); // just in case + Py_DECREF(result); + return PyCom_HandleIEnumNoSequence(IID_IEnum{enumtype}); }} STDMETHODIMP PyGEnum{enumtype}::Skip( /* [in] */ ULONG celt) {{ - PY_GATEWAY_METHOD; - return InvokeViaPolicy("Skip", NULL, "i", celt); + PY_GATEWAY_METHOD; + return InvokeViaPolicy("Skip", NULL, "i", celt); }} STDMETHODIMP PyGEnum{enumtype}::Reset(void) {{ - PY_GATEWAY_METHOD; - return InvokeViaPolicy("Reset"); + PY_GATEWAY_METHOD; + return InvokeViaPolicy("Reset"); }} STDMETHODIMP PyGEnum{enumtype}::Clone( /* [out] */ IEnum{enumtype} __RPC_FAR *__RPC_FAR *ppEnum) {{ - PY_GATEWAY_METHOD; - PyObject * result; - HRESULT hr = InvokeViaPolicy("Clone", &result); - if ( FAILED(hr) ) - return hr; - - /* - ** Make sure we have the right kind of object: we should have some kind - ** of IUnknown subclass wrapped into a PyIUnknown instance. - */ - if ( !PyIBase::is_object(result, &PyIUnknown::type) ) - {{ - /* the wrong kind of object was returned to us */ - Py_DECREF(result); - return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum{enumtype}); - }} - - /* - ** Get the IUnknown out of the thing. note that the Python ob maintains - ** a reference, so we don't have to explicitly AddRef() here. - */ - IUnknown *punk = ((PyIUnknown *)result)->m_obj; - if ( !punk ) - {{ - /* damn. the object was released. */ - Py_DECREF(result); - return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum{enumtype}); - }} - - /* - ** Get the interface we want. note it is returned with a refcount. - ** This QI is actually going to instantiate a PyGEnum{enumtype}. - */ - hr = punk->QueryInterface(IID_IEnum{enumtype}, (LPVOID *)ppEnum); - - /* done with the result; this DECREF is also for */ - Py_DECREF(result); - - return PyCom_CheckIEnumNextResult(hr, IID_IEnum{enumtype}); + PY_GATEWAY_METHOD; + PyObject * result; + HRESULT hr = InvokeViaPolicy("Clone", &result); + if ( FAILED(hr) ) + return hr; + + /* + ** Make sure we have the right kind of object: we should have some kind + ** of IUnknown subclass wrapped into a PyIUnknown instance. + */ + if ( !PyIBase::is_object(result, &PyIUnknown::type) ) + {{ + /* the wrong kind of object was returned to us */ + Py_DECREF(result); + return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum{enumtype}); + }} + + /* + ** Get the IUnknown out of the thing. note that the Python ob maintains + ** a reference, so we don't have to explicitly AddRef() here. + */ + IUnknown *punk = ((PyIUnknown *)result)->m_obj; + if ( !punk ) + {{ + /* damn. the object was released. */ + Py_DECREF(result); + return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum{enumtype}); + }} + + /* + ** Get the interface we want. note it is returned with a refcount. + ** This QI is actually going to instantiate a PyGEnum{enumtype}. + */ + hr = punk->QueryInterface(IID_IEnum{enumtype}, (LPVOID *)ppEnum); + + /* done with the result; this DECREF is also for */ + Py_DECREF(result); + + return PyCom_CheckIEnumNextResult(hr, IID_IEnum{enumtype}); }} """.format( **locals() diff --git a/com/win32com/makegw/makegwparse.py b/com/win32com/makegw/makegwparse.py index 0090c558b7..a1dff12ab1 100644 --- a/com/win32com/makegw/makegwparse.py +++ b/com/win32com/makegw/makegwparse.py @@ -11,6 +11,7 @@ See the @win32com.makegw@ module for information in building a COM interface """ + import re import traceback @@ -34,7 +35,7 @@ def __init__(self, msg="The required functionality is not supported"): class ArgFormatter: - """An instance for a specific type of argument. Knows how to convert itself""" + """An instance for a specific type of argument. Knows how to convert itself""" def __init__(self, arg, builtinIndirection, declaredIndirection=0): # print("init:", arg.name, builtinIndirection, declaredIndirection, arg.indirectionLevel) @@ -213,7 +214,7 @@ def GetAutoduckString(self): ) def _GetPythonTypeDesc(self): - "Returns a string with the description of the type. Used for doco purposes" + "Returns a string with the description of the type. Used for doco purposes" return None def NeedUSES_CONVERSION(self): @@ -758,6 +759,14 @@ def _GetPythonTypeDesc(self): "LPITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "LPCITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "const ITEMIDLIST": (ArgFormatterIDLIST, 0, 1), + "PITEMID_CHILD": (ArgFormatterIDLIST, 1), + "const PITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PCITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PUITEMID_CHILD": (ArgFormatterIDLIST, 1), + "PCUITEMID_CHILD": (ArgFormatterIDLIST, 0), + "const PUITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PCUITEMID_CHILD_ARRAY": (ArgFormatterIDLIST, 2), + "const PCUITEMID_CHILD_ARRAY": (ArgFormatterIDLIST, 2), } # Auto-add all the simple types @@ -795,11 +804,20 @@ class Argument: # -------------- -------- ------------ ------ regex = re.compile(r"/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]") + @classmethod + def drop_rpc_metadata(cls, type_): + return " ".join(e for e in type_.split(" ") if not e.startswith("__RPC_")) + def __init__(self, good_interface_names): self.good_interface_names = good_interface_names - self.inout = self.name = self.type = None + self.inout = None + self.name = None + self.type = None + self.raw_type = None + self.unc_type = None self.const = 0 self.arrayDecl = 0 + self.indirectionLevel = 0 def BuildFromFile(self, file): """Parse and build my data from a file @@ -816,21 +834,17 @@ def BuildFromFile(self, file): self.inout = mo.group(1).split("][") typ = mo.group(2).strip() self.raw_type = typ - self.indirectionLevel = 0 if mo.group(4): # Has "[ ]" decl self.arrayDecl = 1 - try: - pos = typ.rindex("__RPC_FAR") - self.indirectionLevel = self.indirectionLevel + 1 - typ = typ[:pos].strip() - except ValueError: - pass + typ_no_rpc = self.drop_rpc_metadata(typ) + if typ != typ_no_rpc: + self.indirectionLevel += 1 - typ = typ.replace("__RPC_FAR", "") + typ = self.drop_rpc_metadata(typ) while 1: try: pos = typ.rindex("*") - self.indirectionLevel = self.indirectionLevel + 1 + self.indirectionLevel += 1 typ = typ[:pos].strip() except ValueError: break @@ -842,7 +856,7 @@ def BuildFromFile(self, file): if VERBOSE: print( - " Arg {} of type {}{} ({})".format( + " Arg {} of type {}{} ({})".format( self.name, self.type, "*" * self.indirectionLevel, self.inout ) ) @@ -902,7 +916,7 @@ def BuildFromFile(self, file): "Method %s - Only HRESULT return types are supported." % self.name ) # raise error_not_supported, if VERBOSE: - print(f" Method {self.result} {self.name}(") + print(f" Method {self.result} {self.name}(") while 1: arg = Argument(self.good_interface_names) try: diff --git a/com/win32com/server/connect.py b/com/win32com/server/connect.py index 5c9a2dfe4a..6efe4f7111 100644 --- a/com/win32com/server/connect.py +++ b/com/win32com/server/connect.py @@ -2,12 +2,13 @@ A collection of helpers for server side connection points. """ + import pythoncom import win32com.server.util import winerror from win32com import olectl -from .exception import Exception +from .exception import COMException # Methods implemented by the interfaces. IConnectionPointContainer_methods = ["EnumConnectionPoints", "FindConnectionPoint"] @@ -34,10 +35,10 @@ def __init__(self): # IConnectionPoint interfaces def EnumConnections(self): - raise Exception(winerror.E_NOTIMPL) + raise COMException(winerror.E_NOTIMPL) def GetConnectionInterface(self): - raise Exception(winerror.E_NOTIMPL) + raise COMException(winerror.E_NOTIMPL) def GetConnectionPointContainer(self): return win32com.server.util.wrap(self) @@ -50,7 +51,7 @@ def Advise(self, pUnk): self._connect_interfaces_[0], pythoncom.IID_IDispatch ) except pythoncom.com_error: - raise Exception(scode=olectl.CONNECT_E_NOCONNECTION) + raise COMException(scode=olectl.CONNECT_E_NOCONNECTION) self.cookieNo = self.cookieNo + 1 self.connections[self.cookieNo] = interface return self.cookieNo @@ -60,11 +61,11 @@ def Unadvise(self, cookie): try: del self.connections[cookie] except KeyError: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) # IConnectionPointContainer interfaces def EnumConnectionPoints(self): - raise Exception(winerror.E_NOTIMPL) + raise COMException(winerror.E_NOTIMPL) def FindConnectionPoint(self, iid): # Find a connection we support. Only support the single event interface. diff --git a/com/win32com/server/dispatcher.py b/com/win32com/server/dispatcher.py index 7442e001cc..ca97f41138 100644 --- a/com/win32com/server/dispatcher.py +++ b/com/win32com/server/dispatcher.py @@ -2,6 +2,9 @@ Please see policy.py for a discussion on dispatchers and policies """ + +from __future__ import annotations + import traceback from sys import exc_info @@ -294,6 +297,6 @@ def _HandleException_(self): try: import win32trace - DefaultDebugDispatcher = DispatcherWin32trace + DefaultDebugDispatcher: type[DispatcherTrace] = DispatcherWin32trace except ImportError: # no win32trace module - just use a print based one. DefaultDebugDispatcher = DispatcherTrace diff --git a/com/win32com/server/exception.py b/com/win32com/server/exception.py index 599636e730..887186b4d4 100644 --- a/com/win32com/server/exception.py +++ b/com/win32com/server/exception.py @@ -2,22 +2,23 @@ Exceptions - To better support COM exceptions, the framework allows for an instance to be - raised. This instance may have a certain number of known attributes, which are - translated into COM exception details. - - This means, for example, that Python could raise a COM exception that includes details - on a Help file and location, and a description for the user. - - This module provides a class which provides the necessary attributes. + To better support COM exceptions, the framework allows for an instance to be + raised. This instance may have a certain number of known attributes, which are + translated into COM exception details. + + This means, for example, that Python could raise a COM exception that includes details + on a Help file and location, and a description for the user. + + This module provides a class which provides the necessary attributes. """ + import sys import pythoncom -# Note that we derive from com_error, which derives from exceptions.Exception +# Note that we derive from com_error, which derives from builtin Exception # Also note that we dont support "self.args", as we dont support tuple-unpacking class COMException(pythoncom.com_error): """An Exception object that is understood by the framework. @@ -81,25 +82,19 @@ def __repr__(self): return f"" -# Old name for the COMException class. -# Do NOT use the name Exception, as it is now a built-in -# COMException is the new, official name. -Exception = COMException - - def IsCOMException(t=None): if t is None: t = sys.exc_info()[0] - try: - return issubclass(t, pythoncom.com_error) - except TypeError: # 1.5 in -X mode? - return t is pythoncon.com_error + if not t is type: + # t is not a class (likely None or a str) + return False + return issubclass(t, pythoncom.com_error) def IsCOMServerException(t=None): if t is None: t = sys.exc_info()[0] - try: - return issubclass(t, COMException) - except TypeError: # String exception - return 0 + if not t is type: + # t is not a class (likely None or a str) + return False + return issubclass(t, COMException) diff --git a/com/win32com/server/policy.py b/com/win32com/server/policy.py index 7d46d83d02..a66fc0ca5f 100644 --- a/com/win32com/server/policy.py +++ b/com/win32com/server/policy.py @@ -46,13 +46,13 @@ not be Python. Therefore, general Python exceptions and tracebacks aren't much use. - In general, there is an Exception class that should be raised, to allow + In general, there is an COMException class that should be raised, to allow the framework to extract rich COM type error information. The general rule is that the **only** exception returned from Python COM - Server code should be an Exception instance. Any other Python exception + Server code should be an COMException instance. Any other Python exception should be considered an implementation bug in the server (if not, it - should be handled, and an appropriate Exception instance raised). Any + should be handled, and an appropriate COMException instance raised). Any other exception is considered "unexpected", and a dispatcher may take special action (see Dispatchers above) @@ -66,6 +66,7 @@ problem, rather than a COM error. """ + __author__ = "Greg Stein and Mark Hammond" import sys diff --git a/com/win32com/server/register.py b/com/win32com/server/register.py index 211e7428b6..ad38133176 100644 --- a/com/win32com/server/register.py +++ b/com/win32com/server/register.py @@ -6,6 +6,7 @@ construct the necessary Python object, and dispatch COM events. """ + import os import sys @@ -96,7 +97,7 @@ def _cat_registrar(): def _find_localserver_exe(mustfind): if sys.platform != "win32": return sys.executable - if pythoncom.__file__.find("_d") < 0: + if os.path.splitext(os.path.basename(pythoncom.__file__))[0].endswith("_d"): exeBaseName = "pythonw.exe" else: exeBaseName = "pythonw_d.exe" @@ -234,7 +235,13 @@ def RegisterServer( # Although now we prefer a 'loader' DLL if it exists to avoid some # manifest issues (the 'loader' DLL has a manifest, but pythoncom does not) pythoncom_dir = os.path.dirname(pythoncom.__file__) - suffix = "_d" if "_d" in pythoncom.__file__ else "" + suffix = ( + "_d" + if os.path.splitext(os.path.basename(pythoncom.__file__))[0].endswith( + "_d" + ) + else "" + ) # Always register with the full path to the DLLs. loadername = os.path.join( pythoncom_dir, diff --git a/com/win32com/server/util.py b/com/win32com/server/util.py index 853f03a2b1..d8682460a9 100644 --- a/com/win32com/server/util.py +++ b/com/win32com/server/util.py @@ -1,5 +1,6 @@ """ General Server side utilities """ + import pythoncom import winerror diff --git a/com/win32com/servers/dictionary.py b/com/win32com/servers/dictionary.py index 72b405937e..12cd3be5b1 100644 --- a/com/win32com/servers/dictionary.py +++ b/com/win32com/servers/dictionary.py @@ -31,7 +31,6 @@ next """ - import pythoncom import pywintypes import winerror diff --git a/com/win32com/servers/interp.py b/com/win32com/servers/interp.py index 9636837b85..37d83bd497 100644 --- a/com/win32com/servers/interp.py +++ b/com/win32com/servers/interp.py @@ -12,7 +12,7 @@ """ import winerror -from win32com.server.exception import Exception +from win32com.server.exception import COMException # Expose the Python interpreter. @@ -33,14 +33,18 @@ def __init__(self): def Eval(self, exp): """Evaluate an expression.""" if not isinstance(exp, str): - raise Exception(desc="Must be a string", scode=winerror.DISP_E_TYPEMISMATCH) + raise COMException( + desc="Must be a string", scode=winerror.DISP_E_TYPEMISMATCH + ) return eval(str(exp), self.dict) def Exec(self, exp): """Execute a statement.""" if not isinstance(exp, str): - raise Exception(desc="Must be a string", scode=winerror.DISP_E_TYPEMISMATCH) + raise COMException( + desc="Must be a string", scode=winerror.DISP_E_TYPEMISMATCH + ) exec(str(exp), self.dict) diff --git a/com/win32com/servers/perfmon.py b/com/win32com/servers/perfmon.py index 861c694097..8921c73fa9 100644 --- a/com/win32com/servers/perfmon.py +++ b/com/win32com/servers/perfmon.py @@ -1,13 +1,15 @@ """A COM Server which exposes the NT Performance monitor in a very rudimentary way Usage from VB: - set ob = CreateObject("Python.PerfmonQuery") - freeBytes = ob.Query("Memory", "Available Bytes") + set ob = CreateObject("Python.PerfmonQuery") + freeBytes = ob.Query("Memory", "Available Bytes") """ + import pythoncom import win32pdhutil import winerror -from win32com.server import exception, register +from win32com.server import register +from win32com.server.exception import COMException class PerfMonQuery: @@ -24,9 +26,9 @@ def Query(self, object, counter, instance=None, machine=None): object, counter, instance, machine=machine ) except win32pdhutil.error as exc: - raise exception.Exception(desc=exc.strerror) + raise COMException(desc=exc.strerror) except TypeError as desc: - raise exception.Exception(desc=desc, scode=winerror.DISP_E_TYPEMISMATCH) + raise COMException(desc=desc, scode=winerror.DISP_E_TYPEMISMATCH) if __name__ == "__main__": diff --git a/com/win32com/src/PythonCOMLoader.cpp b/com/win32com/src/PythonCOMLoader.cpp index daca9ac3ad..e9a149748b 100644 --- a/com/win32com/src/PythonCOMLoader.cpp +++ b/com/win32com/src/PythonCOMLoader.cpp @@ -1,5 +1,5 @@ -// *sob* - a simple loader for pythoncomxx.dll - but this DLL has a -// manifest referencing the CRT whereas pythoncomxx.dll does not. +// *sob* - a simple loader for pythoncomXX.dll - but this DLL has a +// manifest referencing the CRT whereas pythoncomXX.dll does not. #include "windows.h" #include "tchar.h" @@ -61,7 +61,7 @@ typedef HRESULT(STDAPICALLTYPE *PFNDllGetClassObject)(REFCLSID rclsid, REFIID ri PFNDllGetClassObject pfnDllGetClassObject = NULL; ///////////////////////////////////////////////////////////////////////////// -// Loads pythoncomxx.dll after activating our context and delegates the call to it. +// Loads pythoncomXX.dll after activating our context and delegates the call to it. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { if (pfnDllGetClassObject == 0) { diff --git a/com/win32com/src/include/PythonCOM.h b/com/win32com/src/include/PythonCOM.h index e759971792..c8b7da82fa 100644 --- a/com/win32com/src/include/PythonCOM.h +++ b/com/win32com/src/include/PythonCOM.h @@ -77,10 +77,10 @@ #define PYCOM_EXPORT #else #ifdef BUILD_PYTHONCOM -/* We are building pythoncomxx.dll */ +/* We are building pythoncomXX.dll */ #define PYCOM_EXPORT __declspec(dllexport) #else -/* This module uses pythoncomxx.dll */ +/* This module uses pythoncomXX.dll */ #define PYCOM_EXPORT __declspec(dllimport) #ifndef _DEBUG #pragma comment(lib, "pythoncom.lib") diff --git a/com/win32com/src/oleargs.cpp b/com/win32com/src/oleargs.cpp index a28bbc0d64..c6fad6c215 100644 --- a/com/win32com/src/oleargs.cpp +++ b/com/win32com/src/oleargs.cpp @@ -127,7 +127,7 @@ BOOL PyCom_VariantFromPyObject(PyObject *obj, VARIANT *var) } V_VT(var) = VT_BSTR; } - // For 3.x, bool checks need to be above PyLong_Check, which now succeeds for booleans. + // For Python 3, bool checks need to be above PyLong_Check, which now succeeds for booleans. else if (obj == Py_True) { V_VT(var) = VT_BOOL; V_BOOL(var) = VARIANT_TRUE; @@ -593,7 +593,7 @@ static BOOL PyCom_SAFEARRAYFromPyObjectBuildDimension(PyObject *obj, SAFEARRAY * static long PyCom_CalculatePyObjectDimension(PyObject *obItemCheck, long lDimension, PyObject *ppyobDimensionDictionary) { // Buffers are a special case - they define 1 new dimension. - // Buffers supported sequence semantics in 2.x, but for some reason memoryview objects + // Buffers supported sequence semantics in Python 2, but for some reason memoryview objects // in py3k do not, so check separately if (PYWIN_BUFFER_CHECK(obItemCheck)) return lDimension + 1; diff --git a/com/win32com/test/pippo_server.py b/com/win32com/test/pippo_server.py index 71c981270a..ac3a00c788 100644 --- a/com/win32com/test/pippo_server.py +++ b/com/win32com/test/pippo_server.py @@ -41,8 +41,10 @@ def Method3(self, in1): def BuildTypelib(): - # https://github.com/pypa/setuptools/pull/4069 - from distutils.dep_util import newer + if sys.version_info >= (3, 8): + from setuptools.modified import newer + else: + from distutils.dep_util import newer this_dir = os.path.dirname(__file__) idl = os.path.abspath(os.path.join(this_dir, "pippo.idl")) diff --git a/com/win32com/test/testDynamic.py b/com/win32com/test/testDynamic.py index ed0c5d1ddf..0d073c4a87 100644 --- a/com/win32com/test/testDynamic.py +++ b/com/win32com/test/testDynamic.py @@ -2,7 +2,7 @@ import pythoncom import winerror -from win32com.server.exception import Exception +from win32com.server.exception import COMException error = "testDynamic error" @@ -22,7 +22,7 @@ def _dynamic_(self, name, lcid, wFlags, args): ret = list(ret) return ret except KeyError: # Probably a method request. - raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND) + raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) if wFlags & ( pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF @@ -30,11 +30,11 @@ def _dynamic_(self, name, lcid, wFlags, args): setattr(self, name, args[0]) return - raise Exception(scode=winerror.E_INVALIDARG, desc="invalid wFlags") + raise COMException(scode=winerror.E_INVALIDARG, desc="invalid wFlags") def write(self, *args): if len(args) == 0: - raise Exception( + raise COMException( scode=winerror.DISP_E_BADPARAMCOUNT ) # Probably call as PROPGET. diff --git a/com/win32com/test/testGIT.py b/com/win32com/test/testGIT.py index 2ec6bc44b9..3f9a5f420a 100644 --- a/com/win32com/test/testGIT.py +++ b/com/win32com/test/testGIT.py @@ -134,7 +134,7 @@ def test(fn): if __name__ == "__main__": test(BeginThreadsSimpleMarshal) win32api.Sleep(500) - # Doing CoUninit here stop Pythoncom.dll hanging when DLLMain shuts-down the process + # Doing CoUninit here stop pythoncom.dll hanging when DLLMain shuts-down the process pythoncom.CoUninitialize() if pythoncom._GetInterfaceCount() != 0 or pythoncom._GetGatewayCount() != 0: print( diff --git a/com/win32com/test/testMSOffice.py b/com/win32com/test/testMSOffice.py index f4893dfde8..8b3e0df9e0 100644 --- a/com/win32com/test/testMSOffice.py +++ b/com/win32com/test/testMSOffice.py @@ -11,7 +11,6 @@ import win32api import win32com import win32com.client.dynamic -from pywintypes import Unicode from win32com.client import gencache from win32com.test.util import CheckClean @@ -112,7 +111,7 @@ def TextExcel(xl): if not xl.Visible: raise error("Visible property not true.") - if int(xl.Version[0]) >= 8: + if int(xl.Version.split(".")[0]) >= 8: xl.Workbooks.Add() else: xl.Workbooks().Add() @@ -127,16 +126,16 @@ def TextExcel(xl): if xl.Range("A1").Value != "Hi 0": raise error("Single cell range failed") - if xl.Range("A1:B1").Value != ((Unicode("Hi 0"), 2),): + if xl.Range("A1:B1").Value != (("Hi 0", 2),): raise error("flat-horizontal cell range failed") - if xl.Range("A1:A2").Value != ((Unicode("Hi 0"),), (Unicode("x"),)): + if xl.Range("A1:A2").Value != (("Hi 0",), ("x",)): raise error("flat-vertical cell range failed") if xl.Range("A1:C3").Value != ( - (Unicode("Hi 0"), 2, 3), - (Unicode("x"), Unicode("Hi 1"), Unicode("z")), - (3, 2, Unicode("Hi 2")), + ("Hi 0", 2, 3), + ("x", "Hi 1", "z"), + (3, 2, "Hi 2"), ): raise error("square cell range failed") @@ -144,7 +143,7 @@ def TextExcel(xl): if xl.Range("A1:C3").Value != ( (3, 2, 1), - (Unicode("x"), Unicode("y"), Unicode("z")), + ("x", "y", "z"), (1, 2, 3), ): raise error("Range was not what I set it to!") diff --git a/com/win32com/test/testvb.py b/com/win32com/test/testvb.py index bd188ca0c2..02e33b614f 100644 --- a/com/win32com/test/testvb.py +++ b/com/win32com/test/testvb.py @@ -4,6 +4,7 @@ # import traceback +from collections.abc import Callable import pythoncom import win32com.client diff --git a/com/win32com/util.py b/com/win32com/util.py index 767e34a7cb..0a5748f1e1 100644 --- a/com/win32com/util.py +++ b/com/win32com/util.py @@ -2,6 +2,7 @@ This module contains a collection of general purpose utility functions. """ + import pythoncom import win32api import win32con diff --git a/com/win32comext/adsi/__init__.py b/com/win32comext/adsi/__init__.py index 9ba8d4b531..1b464d10a3 100644 --- a/com/win32comext/adsi/__init__.py +++ b/com/win32comext/adsi/__init__.py @@ -25,7 +25,7 @@ # interface, as well as via IDispatch. import pythoncom -from .adsi import * # nopycln: import # win32comext/adsi/adsi.pyd +from .adsi import * # nopycln: import # Re-export everything from win32comext/adsi/adsi.pyd LCID = 0 @@ -51,8 +51,8 @@ def _get_good_ret( class ADSIEnumerator: def __init__(self, ob): # Query the object for the container interface. - self._cont_ = ob.QueryInterface(IID_IADsContainer) - self._oleobj_ = ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT + self._cont_ = ob.QueryInterface(adsi.IID_IADsContainer) + self._oleobj_ = adsi.ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT self.index = -1 def __getitem__(self, index): @@ -68,20 +68,17 @@ def __GetIndex(self, index): # Index requested out of sequence. raise ValueError("You must index this object sequentially") self.index = index - result = ADsEnumerateNext(self._oleobj_, 1) + result = adsi.ADsEnumerateNext(self._oleobj_, 1) if len(result): return _get_good_ret(result[0]) # Failed - reset for next time around. self.index = -1 - self._oleobj_ = ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT + self._oleobj_ = adsi.ADsBuildEnumerator(self._cont_) # a PyIADsEnumVARIANT raise IndexError("list index out of range") class ADSIDispatch(win32com.client.CDispatch): - def _wrap_dispatch_( - self, ob, userName=None, returnCLSID=None, UnicodeToString=None - ): - assert UnicodeToString is None, "this is deprectated and will be removed" + def _wrap_dispatch_(self, ob, userName=None, returnCLSID=None): if not userName: userName = "ADSI-object" olerepr = win32com.client.dynamic.MakeOleRepr(ob, None, None) @@ -105,18 +102,12 @@ def QueryInterface(self, iid): return _get_good_ret(ret) -# We override the global methods to do the right thing. -_ADsGetObject = ADsGetObject # The one in the .pyd - - +# We override the adsi.pyd methods to do the right thing. def ADsGetObject(path, iid=pythoncom.IID_IDispatch): - ret = _ADsGetObject(path, iid) + ret = adsi.ADsGetObject(path, iid) return _get_good_ret(ret) -_ADsOpenObject = ADsOpenObject - - def ADsOpenObject(path, username, password, reserved=0, iid=pythoncom.IID_IDispatch): - ret = _ADsOpenObject(path, username, password, reserved, iid) + ret = adsi.ADsOpenObject(path, username, password, reserved, iid) return _get_good_ret(ret) diff --git a/com/win32comext/adsi/demos/test.py b/com/win32comext/adsi/demos/test.py index 64061ab68d..3d6b47d565 100644 --- a/com/win32comext/adsi/demos/test.py +++ b/com/win32comext/adsi/demos/test.py @@ -1,4 +1,5 @@ import sys +from collections.abc import Callable import pythoncom import win32api diff --git a/com/win32comext/axdebug/Test/host.py b/com/win32comext/axdebug/Test/host.py index b3d1317462..44b6f9a7c7 100644 --- a/com/win32comext/axdebug/Test/host.py +++ b/com/win32comext/axdebug/Test/host.py @@ -6,7 +6,7 @@ import winerror from win32com.axdebug import codecontainer, gateways from win32com.axdebug.util import _wrap, trace -from win32com.server.exception import Exception +from win32com.server.exception import COMException class ExternalConnection: @@ -57,7 +57,7 @@ def _GetCodeContainer(self): try: codeText = open(self.module.__file__, "rt").read() except OSError as details: - codeText = f"# Exception opening file\n# {details}" + codeText = f"# COMException opening file\n# {details}" self.codeContainer = codecontainer.SourceCodeContainer( codeText, self.module.__file__ @@ -79,23 +79,23 @@ def GetDeferredText(self, dwTextStartCookie, maxChars, bWantAttr): def GetScriptTextAttributes(self, codeText, delimterText, flags): # Result must be an attribute sequence of same "length" as the code. trace("GetScriptTextAttributes", delimterText, flags) - raise Exception(scode=winerror.E_NOTIMPL) + raise COMException(scode=winerror.E_NOTIMPL) def OnCreateDocumentContext(self): # Result must be a PyIUnknown trace("OnCreateDocumentContext") - raise Exception(scode=winerror.E_NOTIMPL) + raise COMException(scode=winerror.E_NOTIMPL) def GetPathName(self): # Result must be (string, int) where the int is a BOOL # - TRUE if the path refers to the original file for the document. # - FALSE if the path refers to a newly created temporary file. - # - raise Exception(scode=E_FAIL) if no source file can be created/determined. + # - raise COMException(scode=E_FAIL) if no source file can be created/determined. trace("GetPathName") try: return win32api.GetFullPathName(self.module.__file__), 1 except (AttributeError, win32api.error): - raise Exception(scode=winerror.E_FAIL) + raise COMException(scode=winerror.E_FAIL) def GetFileName(self): # Result is a string with just the name of the document, no path information. @@ -104,7 +104,7 @@ def GetFileName(self): def NotifyChanged(): trace("NotifyChanged") - raise Exception(scode=winerror.E_NOTIMPL) + raise COMException(scode=winerror.E_NOTIMPL) def TestSmartProvider(): diff --git a/com/win32comext/axdebug/adb.py b/com/win32comext/axdebug/adb.py index 7f0984535d..2345ce5d69 100644 --- a/com/win32comext/axdebug/adb.py +++ b/com/win32comext/axdebug/adb.py @@ -1,5 +1,6 @@ """The glue between the Python debugger interface and the Active Debugger interface """ + import _thread import bdb import os diff --git a/com/win32comext/axdebug/codecontainer.py b/com/win32comext/axdebug/codecontainer.py index 69a9f8e458..2030cf6954 100644 --- a/com/win32comext/axdebug/codecontainer.py +++ b/com/win32comext/axdebug/codecontainer.py @@ -12,7 +12,7 @@ import winerror from win32com.axdebug import axdebug, contexts from win32com.axdebug.util import _wrap -from win32com.server.exception import Exception +from win32com.server.exception import COMException _keywords = {} # set of Python keywords for name in """ @@ -65,7 +65,7 @@ def GetPositionOfLine(self, cLineNumber): try: return self.lineOffsets[cLineNumber] except IndexError: - raise Exception(scode=winerror.S_FALSE) + raise COMException(scode=winerror.S_FALSE) def GetLineOfPosition(self, charPos): self.GetText() # Prime us. @@ -78,7 +78,7 @@ def GetLineOfPosition(self, charPos): lineNo = lineNo + 1 else: # for not broken. # print("Cant find", charPos, "in", self.lineOffsets) - raise Exception(scode=winerror.S_FALSE) + raise COMException(scode=winerror.S_FALSE) # print("GLOP ret=", lineNo, (charPos - lastOffset)) return lineNo, (charPos - lastOffset) @@ -225,7 +225,7 @@ def GetText(self): try: self.text = open(fname, "r").read() except OSError as details: - self.text = f"# Exception opening file\n# {repr(details)}" + self.text = f"# COMException opening file\n# {repr(details)}" else: self.text = f"# No file available for module '{self.module}'" self._buildlines() @@ -248,7 +248,7 @@ def GetName(self, dnt): elif dnt == axdebug.DOCUMENTNAMETYPE_URL: return f"file:{fname}" else: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) if __name__ == "__main__": diff --git a/com/win32comext/axdebug/contexts.py b/com/win32comext/axdebug/contexts.py index 8953f5ab9c..08443b2515 100644 --- a/com/win32comext/axdebug/contexts.py +++ b/com/win32comext/axdebug/contexts.py @@ -1,6 +1,7 @@ """ A module for managing the AXDebug I*Contexts """ + from . import adb, axdebug, gateways # Utility function for wrapping object created by this module. diff --git a/com/win32comext/axdebug/documents.py b/com/win32comext/axdebug/documents.py index 4b93e8c1d0..560901fb42 100644 --- a/com/win32comext/axdebug/documents.py +++ b/com/win32comext/axdebug/documents.py @@ -1,7 +1,6 @@ """ Management of documents for AXDebugging. """ - import pythoncom import win32api from win32com.server.util import unwrap diff --git a/com/win32comext/axdebug/gateways.py b/com/win32comext/axdebug/gateways.py index d87e85b335..19f803eaf6 100644 --- a/com/win32comext/axdebug/gateways.py +++ b/com/win32comext/axdebug/gateways.py @@ -5,7 +5,7 @@ import winerror from win32com.axdebug import axdebug from win32com.axdebug.util import RaiseNotImpl, _wrap -from win32com.server.exception import Exception +from win32com.server.exception import COMException from win32com.server.util import ListEnumeratorGateway @@ -233,7 +233,7 @@ def GetPathName(self): - if fIsOriginalPath is TRUE if the path refers to the original file for the document. - if fIsOriginalPath is FALSE if the path refers to a newly created temporary file. - raise Exception(winerror.E_FAIL) if no source file can be created/determined. + raise COMException(winerror.E_FAIL) if no source file can be created/determined. """ RaiseNotImpl("GetPathName") @@ -398,7 +398,7 @@ def GetPathName(self): # Result must be (string, int) where the int is a BOOL # - TRUE if the path refers to the original file for the document. # - FALSE if the path refers to a newly created temporary file. - # - raise Exception(scode=E_FAIL) if no source file can be created/determined. + # - raise COMException(scode=E_FAIL) if no source file can be created/determined. RaiseNotImpl("GetPathName") def GetFileName(self): @@ -449,7 +449,7 @@ def Unadvise(self, cookie): try: del self.connections[cookie] except KeyError: - return Exception(scode=winerror.E_UNEXPECTED) + return COMException(scode=winerror.E_UNEXPECTED) # IConnectionPointContainer interfaces def EnumConnectionPoints(self): @@ -459,7 +459,7 @@ def FindConnectionPoint(self, iid): # Find a connection we support. Only support the single event interface. if iid == axdebug.IID_IDebugDocumentTextEvents: return _wrap(self) - raise Exception(scode=winerror.E_NOINTERFACE) # ?? + raise COMException(scode=winerror.E_NOINTERFACE) # ?? class RemoteDebugApplicationEvents: diff --git a/com/win32comext/axdebug/util.py b/com/win32comext/axdebug/util.py index 805d21b28b..46806ae547 100644 --- a/com/win32comext/axdebug/util.py +++ b/com/win32comext/axdebug/util.py @@ -7,7 +7,7 @@ import win32api import win32com.server.util import winerror -from win32com.server.exception import Exception +from win32com.server.exception import COMException try: os.environ["DEBUG_AXDEBUG"] @@ -66,7 +66,7 @@ def RaiseNotImpl(who=None): frame = frame.f_back # and raise the exception for COM - raise Exception(scode=winerror.E_NOTIMPL) + raise COMException(scode=winerror.E_NOTIMPL) import win32com.server.policy @@ -101,7 +101,7 @@ def _Invoke_(self, dispid, lcid, wFlags, args): ) # print("Invoke of", dispid, "returning", rc) return rc - except Exception: + except COMException: t, v, tb = sys.exc_info() tb = None # A cycle scode = v.scode diff --git a/com/win32comext/axscript/asputil.py b/com/win32comext/axscript/asputil.py index b61c35373e..ad1c878daa 100644 --- a/com/win32comext/axscript/asputil.py +++ b/com/win32comext/axscript/asputil.py @@ -1,7 +1,7 @@ """A utility module for ASP (Active Server Pages on MS Internet Info Server. Contains: - iif -- A utility function to avoid using "if" statements in ASP <% tags + iif -- A utility function to avoid using "if" statements in ASP <% tags """ diff --git a/com/win32comext/axscript/client/error.py b/com/win32comext/axscript/client/error.py index c4d24db9de..f10c6db574 100644 --- a/com/win32comext/axscript/client/error.py +++ b/com/win32comext/axscript/client/error.py @@ -8,10 +8,10 @@ import traceback import pythoncom -import win32com.server.exception import win32com.server.util import winerror from win32com.axscript import axscript +from win32com.server.exception import COMException debugging = 0 @@ -64,7 +64,7 @@ def GetExceptionInfo(self): return self.exception -class AXScriptException(win32com.server.exception.COMException): +class AXScriptException(COMException): """A class used as a COM exception. Note this has attributes which conform to the standard attributes @@ -74,8 +74,7 @@ class AXScriptException(win32com.server.exception.COMException): def __init__(self, site, codeBlock, exc_type, exc_value, exc_traceback): # set properties base class shares via base ctor... - win32com.server.exception.COMException.__init__( - self, + super().__init__( description="Unknown Exception", scode=winerror.DISP_E_EXCEPTION, source="Python ActiveX Scripting Engine", @@ -248,7 +247,7 @@ def ProcessAXScriptException(scriptingSite, debugManager, exceptionInstance): if result == winerror.S_OK: # If the above returns NOERROR, it is assumed the error has been # correctly registered and the value SCRIPT_E_REPORTED is returned. - ret = win32com.server.exception.COMException(scode=axscript.SCRIPT_E_REPORTED) + ret = COMException(scode=axscript.SCRIPT_E_REPORTED) return ret else: # The error is taken to be unreported and is propagated up the call stack diff --git a/com/win32comext/axscript/client/framework.py b/com/win32comext/axscript/client/framework.py index 93f9d53741..44ef43ac9a 100644 --- a/com/win32comext/axscript/client/framework.py +++ b/com/win32comext/axscript/client/framework.py @@ -6,6 +6,9 @@ There are classes defined for the engine itself, and for ScriptItems """ + +from __future__ import annotations + import re import sys @@ -28,7 +31,7 @@ def RemoveCR(text): SCRIPTTEXT_ISEXPRESSION = 0x00000020 SCRIPTTEXT_ISPERSISTENT = 0x00000040 -from win32com.server.exception import Exception, IsCOMServerException +from win32com.server.exception import COMException, IsCOMServerException from . import error # ax.client.error @@ -113,7 +116,7 @@ def RaiseAssert(scode, desc): """A debugging function that raises an exception considered an "Assertion".""" print("**************** ASSERTION FAILED *******************") print(desc) - raise Exception(desc, scode) + raise COMException(desc, scode) class AXScriptCodeBlock: @@ -174,7 +177,7 @@ def Build(self, typeinfo, funcdesc): class EventSink: """A set of events against an item. Note this is a COM client for connection points.""" - _public_methods_ = [] + _public_methods_: list[str] = [] def __init__(self, myItem, coDispatch): self.events = {} @@ -206,7 +209,7 @@ def _invoke_(self, dispid, lcid, wFlags, args): try: event = self.events[dispid] except: - raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND) + raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # print("Invoke for ", event, "on", self.myScriptItem, " - calling", self.myInvokeMethod) return self.myInvokeMethod(self.myScriptItem, event, lcid, wFlags, args) @@ -403,10 +406,10 @@ def GetCreateSubItem(self, parentItem, name, dispatch, flags): rc = self.subItems[keyName] # No changes allowed to existing flags. if not rc.flags is None and not flags is None and rc.flags != flags: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) # Existing item must not have a dispatch. if not rc.dispatch is None and not dispatch is None: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) rc.flags = flags # Setup the real flags. rc.dispatch = dispatch except KeyError: @@ -769,7 +772,7 @@ def SetScriptSite(self, site): def GetScriptSite(self, iid): if self.scriptSite is None: - raise Exception(scode=winerror.S_FALSE) + raise COMException(scode=winerror.S_FALSE) return self.scriptSite.QueryInterface(iid) def SetScriptState(self, state): @@ -778,7 +781,7 @@ def SetScriptState(self, state): return # If closed, allow no other state transitions if self.scriptState == axscript.SCRIPTSTATE_CLOSED: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) if state == axscript.SCRIPTSTATE_INITIALIZED: # Re-initialize - shutdown then reset. @@ -820,7 +823,7 @@ def SetScriptState(self, state): self.Reset() self.ChangeScriptState(state) else: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) def GetScriptState(self): return self.scriptState @@ -861,12 +864,12 @@ def Close(self): def AddNamedItem(self, name, flags): if self.scriptSite is None: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) try: unknown = self.scriptSite.GetItemInfo(name, axscript.SCRIPTINFO_IUNKNOWN)[0] dispatch = unknown.QueryInterface(pythoncom.IID_IDispatch) except pythoncom.com_error: - raise Exception( + raise COMException( scode=winerror.E_NOINTERFACE, desc="Object has no dispatch interface available.", ) @@ -878,23 +881,23 @@ def AddNamedItem(self, name, flags): def GetScriptDispatch(self, name): # Base classes should override. - raise Exception(scode=winerror.E_NOTIMPL) + raise COMException(scode=winerror.E_NOTIMPL) def GetCurrentScriptThreadID(self): return self.baseThreadId def GetScriptThreadID(self, win32ThreadId): if self.baseThreadId == -1: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) if self.baseThreadId != win32ThreadId: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) return self.baseThreadId def GetScriptThreadState(self, scriptThreadId): if self.baseThreadId == -1: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) if scriptThreadId != self.baseThreadId: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) return self.threadState def AddTypeLib(self, uuid, major, minor, flags): @@ -906,10 +909,10 @@ def AddTypeLib(self, uuid, major, minor, flags): # This is never called by the C++ framework - it does magic. # See PyGActiveScript.cpp # def InterruptScriptThread(self, stidThread, exc_info, flags): - # raise Exception("Not Implemented", scode=winerror.E_NOTIMPL) + # raise COMException("Not Implemented", scode=winerror.E_NOTIMPL) def Clone(self): - raise Exception("Not Implemented", scode=winerror.E_NOTIMPL) + raise COMException("Not Implemented", scode=winerror.E_NOTIMPL) # # IObjectSafety @@ -939,7 +942,7 @@ def SetInterfaceSafetyOptions(self, iid, optionsMask, enabledOptions): supported = self._GetSupportedInterfaceSafetyOptions() self.safetyOptions = supported & optionsMask & enabledOptions else: - raise Exception(scode=winerror.E_NOINTERFACE) + raise COMException(scode=winerror.E_NOINTERFACE) def _GetSupportedInterfaceSafetyOptions(self): return 0 @@ -955,7 +958,7 @@ def GetInterfaceSafetyOptions(self, iid): supported = self._GetSupportedInterfaceSafetyOptions() return supported, self.safetyOptions else: - raise Exception(scode=winerror.E_NOINTERFACE) + raise COMException(scode=winerror.E_NOINTERFACE) # # Other helpers. @@ -1021,7 +1024,7 @@ def Run(self): self.scriptState != axscript.SCRIPTSTATE_INITIALIZED and self.scriptState != axscript.SCRIPTSTATE_STARTED ): - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) # self._DumpNamedItems_() self.ExecutePendingScripts() self.DoRun() @@ -1199,7 +1202,7 @@ def HandleException(self, codeBlock): ): # Ensure the traceback doesnt cause a cycle. exc_traceback = None - raise Exception(scode=exc_value.hresult) + raise COMException(scode=exc_value.hresult) exception = error.AXScriptException( self, codeBlock, exc_type, exc_value, exc_traceback @@ -1226,12 +1229,12 @@ def HandleException(self, codeBlock): def BeginScriptedSection(self): if self.scriptSite is None: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) self.scriptSite.OnEnterScript() def EndScriptedSection(self): if self.scriptSite is None: - raise Exception(scode=winerror.E_UNEXPECTED) + raise COMException(scode=winerror.E_UNEXPECTED) self.scriptSite.OnLeaveScript() def DisableInterrupts(self): @@ -1244,7 +1247,7 @@ def GetNamedItem(self, name): try: return self.subItems[name] except KeyError: - raise Exception(scode=winerror.E_INVALIDARG) + raise COMException(scode=winerror.E_INVALIDARG) def GetNamedItemClass(self): return ScriptItem diff --git a/com/win32comext/axscript/client/pydumper.py b/com/win32comext/axscript/client/pydumper.py index 3d0725aeff..39bf892845 100644 --- a/com/win32comext/axscript/client/pydumper.py +++ b/com/win32comext/axscript/client/pydumper.py @@ -16,7 +16,7 @@ from win32com.axscript import axscript from . import pyscript -from .pyscript import SCRIPTTEXT_FORCEEXECUTION, Exception, RaiseAssert, trace +from .pyscript import SCRIPTTEXT_FORCEEXECUTION, RaiseAssert, trace PyDump_CLSID = "{ac527e60-c693-11d0-9c25-00aa00125a98}" diff --git a/com/win32comext/axscript/client/pyscript.py b/com/win32comext/axscript/client/pyscript.py index 0ce08fc35e..d53559e300 100644 --- a/com/win32comext/axscript/client/pyscript.py +++ b/com/win32comext/axscript/client/pyscript.py @@ -22,10 +22,10 @@ SCRIPTTEXT_FORCEEXECUTION, SCRIPTTEXT_ISEXPRESSION, SCRIPTTEXT_ISPERSISTENT, - Exception, RaiseAssert, trace, ) +from win32com.server.exception import COMException PyScript_CLSID = "{DF630910-1C1D-11d0-AE36-8C0F5E000000}" @@ -252,9 +252,9 @@ def RegisterNamedItem(self, item): if item.IsGlobal(): # Global items means sub-items are also added... for subitem in item.subItems.values(): - self.globalNameSpaceModule.__dict__[ - subitem.name - ] = subitem.attributeObject + self.globalNameSpaceModule.__dict__[subitem.name] = ( + subitem.attributeObject + ) # Also add all methods for name, entry in item.dispatchContainer._olerepr_.mapFuncs.items(): if not entry.hidden: @@ -366,7 +366,7 @@ def DoProcessScriptItemEvent(self, item, event, lcid, wFlags, args): item.scriptlets[funcName] = function if function is None: - raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND) + raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) return self.ApplyInScriptedSection(codeBlock, function, args) def DoParseScriptText( diff --git a/com/win32comext/axscript/client/scriptdispatch.py b/com/win32comext/axscript/client/scriptdispatch.py index c1989dc286..2c15d74367 100644 --- a/com/win32comext/axscript/client/scriptdispatch.py +++ b/com/win32comext/axscript/client/scriptdispatch.py @@ -5,6 +5,8 @@ this yet, so it is not well tested! """ +from __future__ import annotations + import types import pythoncom @@ -26,7 +28,7 @@ def _is_callable(obj): class ScriptDispatch: - _public_methods_ = [] + _public_methods_: list[str] = [] def __init__(self, engine, scriptNamespace): self.engine = engine diff --git a/com/win32comext/axscript/server/axsite.py b/com/win32comext/axscript/server/axsite.py index 29fa0ebe93..f907789397 100644 --- a/com/win32comext/axscript/server/axsite.py +++ b/com/win32comext/axscript/server/axsite.py @@ -2,7 +2,8 @@ import win32com.axscript.axscript import winerror from win32com.axscript import axscript -from win32com.server import exception, util +from win32com.server import util +from win32com.server.exception import COMException class AXEngine: @@ -115,7 +116,7 @@ def GetLCID(self): def GetItemInfo(self, name, returnMask): if name not in self.objModel: - raise exception.Exception( + raise COMException( scode=winerror.TYPE_E_ELEMENTNOTFOUND, desc="item not found" ) diff --git a/com/win32comext/axscript/server/error.py b/com/win32comext/axscript/server/error.py index 82491a6ebe..4ae196a96a 100644 --- a/com/win32comext/axscript/server/error.py +++ b/com/win32comext/axscript/server/error.py @@ -8,7 +8,7 @@ """ -class Exception: +class Exception: # unused and shadows builtin Exception def __init__(self, activeScriptError): self.activeScriptError = activeScriptError diff --git a/com/win32comext/axscript/test/leakTest.py b/com/win32comext/axscript/test/leakTest.py index b75948eaed..98a589d3ef 100644 --- a/com/win32comext/axscript/test/leakTest.py +++ b/com/win32comext/axscript/test/leakTest.py @@ -4,7 +4,6 @@ import win32com.server.policy from win32com.axscript import axscript from win32com.axscript.server import axsite -from win32com.axscript.server.error import Exception from win32com.server import connect, util diff --git a/com/win32comext/axscript/test/testHost.py b/com/win32comext/axscript/test/testHost.py index ee6210fa65..e0b9995acb 100644 --- a/com/win32comext/axscript/test/testHost.py +++ b/com/win32comext/axscript/test/testHost.py @@ -6,10 +6,8 @@ import win32com.test.util from win32com.axscript import axscript from win32com.axscript.server import axsite -from win32com.axscript.server.error import Exception from win32com.client.dynamic import Dispatch from win32com.server import connect, util -from win32com.server.exception import COMException verbose = "-v" in sys.argv diff --git a/com/win32comext/axscript/test/testHost4Dbg.py b/com/win32comext/axscript/test/testHost4Dbg.py index 2092b045c0..c618a41206 100644 --- a/com/win32comext/axscript/test/testHost4Dbg.py +++ b/com/win32comext/axscript/test/testHost4Dbg.py @@ -6,7 +6,6 @@ import win32ui from win32com.axscript import axscript from win32com.axscript.server import axsite -from win32com.axscript.server.error import Exception from win32com.server import util version = "0.0.1" diff --git a/com/win32comext/mapi/mapiutil.py b/com/win32comext/mapi/mapiutil.py index bf13788076..8716a650a9 100644 --- a/com/win32comext/mapi/mapiutil.py +++ b/com/win32comext/mapi/mapiutil.py @@ -1,10 +1,12 @@ # General utilities for MAPI and MAPI objects. +from __future__ import annotations + import pythoncom from pywintypes import TimeType from . import mapi, mapitags -prTable = {} +prTable: dict[int, str] = {} def GetPropTagName(pt): @@ -57,7 +59,7 @@ def GetPropTagName(pt): return ret -mapiErrorTable = {} +mapiErrorTable: dict[int, str] = {} def GetScodeString(hr): @@ -68,7 +70,7 @@ def GetScodeString(hr): return mapiErrorTable.get(hr, pythoncom.GetScodeString(hr)) -ptTable = {} +ptTable: dict[int, str] = {} def GetMapiTypeName(propType, rawType=True): diff --git a/com/win32comext/mapi/src/PyIMsgStore.i b/com/win32comext/mapi/src/PyIMsgStore.i index ddb43229af..a78fb93802 100644 --- a/com/win32comext/mapi/src/PyIMsgStore.i +++ b/com/win32comext/mapi/src/PyIMsgStore.i @@ -79,6 +79,32 @@ PyObject *PyIMsgStore::OpenEntry(PyObject *self, PyObject *args) %} +%native(StoreLogoff) StoreLogoff; +%{ +// @pyswig |StoreLogoff|Enables the orderly logoff of the message store. +PyObject *PyIMsgStore::StoreLogoff(PyObject *self, PyObject *args) +{ + HRESULT _result; + unsigned long flags; + + IMsgStore *_swig_self; + if ((_swig_self=GetI(self))==NULL) return NULL; + // @pyparm int|flags||Bitmask of flags that controls how the message store is closed. + if(!PyArg_ParseTuple(args,"k:StoreLogoff", &flags)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + _result = (HRESULT )_swig_self->StoreLogoff(&flags); + Py_END_ALLOW_THREADS + if (FAILED(_result)) { + return OleSetOleError(_result); + } + + return PyLong_FromLong(flags); +} + +%} + %native(GetReceiveFolder) GetReceiveFolder; %{ // @pyswig , string|GetReceiveFolder|Obtains the folder that was established as the destination for incoming messages of a specified message class or the default receive folder for the message store. diff --git a/com/win32comext/mapi/src/mapi.i b/com/win32comext/mapi/src/mapi.i index 6e769b7f47..fae114b43e 100644 --- a/com/win32comext/mapi/src/mapi.i +++ b/com/win32comext/mapi/src/mapi.i @@ -269,6 +269,16 @@ static PyObject *PyMAPIUninitialize(PyObject *self, PyObject *args) #define MAPI_BEST_ACCESS MAPI_BEST_ACCESS #define MAPI_MODIFY MAPI_MODIFY +#define LOGOFF_NO_WAIT LOGOFF_NO_WAIT +#define LOGOFF_ORDERLY LOGOFF_ORDERLY +#define LOGOFF_PURGE LOGOFF_PURGE +#define LOGOFF_ABORT LOGOFF_ABORT +#define LOGOFF_QUIET LOGOFF_QUIET +#define LOGOFF_COMPLETE LOGOFF_COMPLETE +#define LOGOFF_INBOUND LOGOFF_INBOUND +#define LOGOFF_OUTBOUND LOGOFF_OUTBOUND +#define LOGOFF_OUTBOUND_QUEUE LOGOFF_OUTBOUND_QUEUE + #define MAPI_DEFERRED_ERRORS MAPI_DEFERRED_ERRORS // Allows a method to return successfully, possibly before the changes have been fully committed. #define MAPI_INIT_VERSION MAPI_INIT_VERSION diff --git a/com/win32comext/mapi/src/mapi_stub_library/StubUtils.cpp b/com/win32comext/mapi/src/mapi_stub_library/StubUtils.cpp index 548835d33f..707bd2a9d8 100644 --- a/com/win32comext/mapi/src/mapi_stub_library/StubUtils.cpp +++ b/com/win32comext/mapi/src/mapi_stub_library/StubUtils.cpp @@ -174,7 +174,7 @@ HMODULE LoadMailClientFromMSIData(HKEY hkeyMapiClient) /* * LoadMAPIFromSystemDir - * Fall back for loading System32\Mapi32.dll if all else fails + * Fall back for loading System32\mapi32.dll if all else fails */ HMODULE LoadMAPIFromSystemDir() { diff --git a/com/win32comext/propsys/src/propsys.cpp b/com/win32comext/propsys/src/propsys.cpp index 3172692c12..613f540fba 100644 --- a/com/win32comext/propsys/src/propsys.cpp +++ b/com/win32comext/propsys/src/propsys.cpp @@ -421,7 +421,7 @@ static PyObject *PyPSGetPropertyFromPropertyStorage(PyObject *self, PyObject *ar PROPERTYKEY key; PROPVARIANT val; PyObject *obbuf; - // @pyparm buffer|ps||Bytes or buffer (or str in python 2) containing a serialized property set (see ) // @pyparm |key||Property to return if (!PyArg_ParseTuple(args, "OO&:PSGetPropertyFromPropertyStorage", &obbuf, PyWinObject_AsPROPERTYKEY, &key)) @@ -446,7 +446,7 @@ static PyObject *PyPSGetNamedPropertyFromPropertyStorage(PyObject *self, PyObjec TmpWCHAR name; PROPVARIANT val; PyObject *obname, *obbuf; - // @pyparm buffer|ps||Bytes or buffer (or str in python 2) containing a serialized property set (see ) // @pyparm str|name||Property to return if (!PyArg_ParseTuple(args, "OO:PSGetNamedPropertyFromPropertyStorage", &obbuf, &obname)) diff --git a/com/win32comext/shell/demos/IFileOperationProgressSink.py b/com/win32comext/shell/demos/IFileOperationProgressSink.py index e9108bbd0b..b327707404 100644 --- a/com/win32comext/shell/demos/IFileOperationProgressSink.py +++ b/com/win32comext/shell/demos/IFileOperationProgressSink.py @@ -134,7 +134,7 @@ def PostDeleteItem(self, Flags, Item, hrDelete, NewlyCreated): ) if NewlyCreated: print( - " Moved to recycle bin - " + " Moved to recycle bin - " + NewlyCreated.GetDisplayName(shellcon.SHGDN_FORPARSING) ) diff --git a/com/win32comext/shell/shellcon.py b/com/win32comext/shell/shellcon.py index 769adf7379..37841a9710 100644 --- a/com/win32comext/shell/shellcon.py +++ b/com/win32comext/shell/shellcon.py @@ -229,6 +229,12 @@ SVSI_ENSUREVISIBLE = 8 SVSI_FOCUSED = 16 SVSI_TRANSLATEPT = 32 +SVSI_SELECTIONMARK = 64 +SVSI_POSITIONITEM = 128 +SVSI_CHECK = 256 +SVSI_CHECK2 = 512 +SVSI_KEYBOARDSELECT = 1025 +SVSI_NOTAKEFOCUS = 1073741824 SVGIO_BACKGROUND = 0 SVGIO_SELECTION = 1 SVGIO_ALLVIEW = 2 @@ -415,6 +421,18 @@ QIF_CACHED = 1 QIF_DONTEXPANDFOLDER = 2 +# ShellWindowFindWindowOptions +SWFO_NEEDDISPATCH = 1 +SWFO_INCLUDEPENDING = 2 +SWFO_COOKIEPASSED = 4 + +# ShellWindowTypeConstants +SWC_EXPLORER = 0 +SWC_BROWSER = 1 +SWC_3RDPARTY = 2 +SWC_CALLBACK = 4 +SWC_DESKTOP = 8 + # SHARD enum for SHAddToRecentDocs SHARD_PIDL = 1 SHARD_PATHA = 2 diff --git a/com/win32comext/shell/src/PyIFolderView.cpp b/com/win32comext/shell/src/PyIFolderView.cpp new file mode 100644 index 0000000000..e9de6b8b69 --- /dev/null +++ b/com/win32comext/shell/src/PyIFolderView.cpp @@ -0,0 +1,432 @@ +// This file implements the IFolderView Interface for Python. +// Generated by makegw.py + +#include "shell_pch.h" +#include "PyIFolderView.h" + +// Helper functions + +static PyObject *PyObject_FromPOINT(const POINT &p) { return Py_BuildValue("ll", p.x, p.y); } + +static BOOL toPIDL(PyObject *pObjId, LPITEMIDLIST *ppidl, IFolderView *self) +{ + if (PyLong_Check(pObjId)) { + int idx = (int)PyLong_AsLong(pObjId); + HRESULT hr = self->Item(idx, ppidl); + if (FAILED(hr)) { + PyCom_BuildPyException(hr, self, IID_IFolderView); + return FALSE; + } + } + else if (PyBytes_Check(pObjId)) { + // Gainarie: Create an "artificial" list ot be passed to PyObject_AsPIDL + PyObject *list = PyList_New(1); + if (list == NULL) + return FALSE; + Py_INCREF(pObjId); + PyList_SET_ITEM(list, 0, pObjId); + BOOL res = PyObject_AsPIDL(list, ppidl); + Py_DECREF(list); + if (!res) + return FALSE; + } + else { + PyErr_Format(PyExc_TypeError, "Integer (index) or bytes (PIDL) required"); + return FALSE; + } + return TRUE; +} + +// @doc - This file contains autoduck documentation +// --------------------------------------------------- +// +// Interface Implementation + +PyIFolderView::PyIFolderView(IUnknown *pdisp) : PyIUnknown(pdisp) { ob_type = &type; } + +PyIFolderView::~PyIFolderView() {} + +/* static */ IFolderView *PyIFolderView::GetI(PyObject *self) { return (IFolderView *)PyIUnknown::GetI(self); } + +// @pymethod |PyIFolderView|GetCurrentViewMode|Description of GetCurrentViewMode. +PyObject *PyIFolderView::GetCurrentViewMode(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + UINT viewMode; + if (!PyArg_ParseTuple(args, ":GetCurrentViewMode")) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetCurrentViewMode(&viewMode); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return Py_BuildValue("I", viewMode); +} + +// @pymethod |PyIFolderView|SetCurrentViewMode|Description of SetCurrentViewMode. +PyObject *PyIFolderView::SetCurrentViewMode(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|ViewMode||Description for ViewMode + UINT viewMode; + if (!PyArg_ParseTuple(args, "I:SetCurrentViewMode", &viewMode)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->SetCurrentViewMode(viewMode); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + Py_RETURN_NONE; +} + +// @pymethod |PyIFolderView|GetFolder|Description of GetFolder. +PyObject *PyIFolderView::GetFolder(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm |riid||Description for riid + PyObject *obriid; + IID riid; + if (!PyArg_ParseTuple(args, "O:GetFolder", &obriid)) + return NULL; + if (!PyWinObject_AsIID(obriid, &riid)) + return NULL; + HRESULT hr; + IShellFolder *ppshf = NULL; + PY_INTERFACE_PRECALL; + hr = pIFV->GetFolder(riid, (void **)&ppshf); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return PyCom_PyObjectFromIUnknown(ppshf, IID_IShellFolder, FALSE); +} + +// @pymethod |PyIFolderView|Item|Description of Item. +PyObject *PyIFolderView::Item(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|iItemIndex||Description for iItemIndex + int iItemIndex; + PITEMID_CHILD pidl = NULL; + if (!PyArg_ParseTuple(args, "i:Item", &iItemIndex)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->Item(iItemIndex, &pidl); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + PyObject *obppidl; + obppidl = PyObject_FromPIDL(pidl, TRUE); + if ((obppidl == Py_None) || (!PyList_Check(obppidl)) || (PyList_Size(obppidl) != 1)) { + Py_XDECREF(obppidl); + Py_RETURN_NONE; + } + PyObject *pyretval = PyList_GetItem(obppidl, 0); + Py_INCREF(pyretval); + Py_DECREF(obppidl); + return pyretval; +} + +// @pymethod |PyIFolderView|ItemCount|Description of ItemCount. +PyObject *PyIFolderView::ItemCount(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|uFlags||Description for uFlags + UINT uFlags; + int pcItems; + if (!PyArg_ParseTuple(args, "I:ItemCount", &uFlags)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->ItemCount(uFlags, &pcItems); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return Py_BuildValue("i", pcItems); +} + +// @pymethod |PyIFolderView|Items|Description of Items. +PyObject *PyIFolderView::Items(PyObject *self, PyObject *args) +{ + return PyErr_Format(PyExc_NotImplementedError, "Not implemented."); + /* + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|uFlags||Description for uFlags + // @pyparm |riid||Description for riid + // *** The input argument ppv of type "__RPC__deref_out_opt void **" was not processed *** + // Please check the conversion function is appropriate and exists! + void ppv; + PyObject *obppv; + // @pyparm |ppv||Description for ppv + PyObject *obriid; + UINT uFlags; + IID riid; + if (!PyArg_ParseTuple(args, "iOO:Items", &uFlags, &obriid, &obppv)) + return NULL; + BOOL bPythonIsHappy = TRUE; + if (!PyWinObject_AsIID(obriid, &riid)) + bPythonIsHappy = FALSE; + if (bPythonIsHappy && !PyObject_Asvoid(obppv, &ppv)) + bPythonIsHappy = FALSE; + if (!bPythonIsHappy) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->Items(uFlags, riid, ppv); + PyObject_Freevoid(ppv); + + PY_INTERFACE_POSTCALL; + + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + // *** The output argument ppv of type "__RPC__deref_out_opt void **" was not processed *** + // The type 'void' (ppv) is unknown. + Py_RETURN_NONE; + //*/ +} + +// @pymethod |PyIFolderView|GetSelectionMarkedItem|Description of GetSelectionMarkedItem. +PyObject *PyIFolderView::GetSelectionMarkedItem(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + int piItem; + if (!PyArg_ParseTuple(args, ":GetSelectionMarkedItem")) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetSelectionMarkedItem(&piItem); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return Py_BuildValue("i", piItem); +} + +// @pymethod |PyIFolderView|GetFocusedItem|Description of GetFocusedItem. +PyObject *PyIFolderView::GetFocusedItem(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + int item; + if (!PyArg_ParseTuple(args, ":GetFocusedItem")) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetFocusedItem(&item); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return Py_BuildValue("i", item); +} + +// @pymethod |PyIFolderView|GetItemPosition|Description of GetItemPosition. +PyObject *PyIFolderView::GetItemPosition(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm |pidl||Description for pidl + POINT pt; + PyObject *obpidl; + PCUITEMID_CHILD pidl = NULL; + if (!PyArg_ParseTuple(args, "O:GetItemPosition", &obpidl)) + return NULL; + if (!toPIDL(obpidl, (LPITEMIDLIST *)&pidl, pIFV)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetItemPosition(pidl, &pt); + PyObject_FreePIDL(pidl); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return PyObject_FromPOINT(pt); +} + +// @pymethod |PyIFolderView|GetSpacing|Description of GetSpacing. +PyObject *PyIFolderView::GetSpacing(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm (int, int)|pt||Coordinates of an item + POINT pt; + if (!PyArg_ParseTuple(args, "ll:GetSpacing", &pt.x, &pt.y)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetSpacing(&pt); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return PyObject_FromPOINT(pt); +} + +// @pymethod |PyIFolderView|GetDefaultSpacing|Description of GetDefaultSpacing. +PyObject *PyIFolderView::GetDefaultSpacing(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + POINT pt; + if (!PyArg_ParseTuple(args, ":GetDefaultSpacing")) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetDefaultSpacing(&pt); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + return PyObject_FromPOINT(pt); +} + +// @pymethod |PyIFolderView|GetAutoArrange|Description of GetAutoArrange. +PyObject *PyIFolderView::GetAutoArrange(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + if (!PyArg_ParseTuple(args, ":GetAutoArrange")) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->GetAutoArrange(); + PY_INTERFACE_POSTCALL; + if (hr == S_OK) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +// @pymethod |PyIFolderView|SelectItem|Description of SelectItem. +PyObject *PyIFolderView::SelectItem(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|iItem||Description for iItem + // @pyparm int|dwFlags||Description for dwFlags + int item; + DWORD dwFlags; + if (!PyArg_ParseTuple(args, "ik:SelectItem", &item, &dwFlags)) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->SelectItem(item, dwFlags); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + Py_RETURN_NONE; +} + +// @pymethod |PyIFolderView|SelectAndPositionItems|Description of SelectAndPositionItems. +PyObject *PyIFolderView::SelectAndPositionItems(PyObject *self, PyObject *args) +{ + return PyErr_Format(PyExc_NotImplementedError, "Not implemented. Use SelectAndPositionItem instead."); + /* + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + // @pyparm int|cidl||Description for cidl + // @pyparm |apidl||Description for apidl + // *** The input argument apt of type "__RPC__in_ecount_full_opt(cidl) POINT *" was not processed *** + // Please check the conversion function is appropriate and exists! + POINT apt; + PyObject *obapt; + // @pyparm |apt||Description for apt + // @pyparm int|dwFlags||Description for dwFlags + PyObject *obapidl; + UINT cidl; + PCUITEMID_CHILD_ARRAY apidl; + DWORD dwFlags; + if (!PyArg_ParseTuple(args, "iOOl:SelectAndPositionItems", &cidl, &obapidl, &obapt, &dwFlags)) + return NULL; + BOOL bPythonIsHappy = TRUE; + if (bPythonIsHappy && !PyObject_AsPIDL(obapidl, &*apidl)) + bPythonIsHappy = FALSE; + if (bPythonIsHappy && !PyObject_AsPOINT(obapt, &apt)) + bPythonIsHappy = FALSE; + if (!bPythonIsHappy) + return NULL; + HRESULT hr; + PY_INTERFACE_PRECALL; + hr = pIFV->SelectAndPositionItems(cidl, apidl, apt, dwFlags); + PyObject_FreePIDL(apidl); + PyObject_FreePOINT(apt); + + PY_INTERFACE_POSTCALL; + + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + Py_RETURN_NONE; +//*/ +} + +// @pymethod |PyIFolderView|SelectAndPositionItem|Description of SelectAndPositionItem. +PyObject *PyIFolderView::SelectAndPositionItem(PyObject *self, PyObject *args) +{ + IFolderView *pIFV = GetI(self); + if (pIFV == NULL) + return NULL; + LPITEMIDLIST pidl = NULL; + PyObject *obpidl; + POINT pt; + // @pyparm |apidl||Description for apidl + // @pyparm (int, int)|pt||Coordinates where drag operation entered the window + // @pyparm int|dwFlags||Description for dwFlags + DWORD dwFlags; + if (!PyArg_ParseTuple(args, "O(ll)k:SelectAndPositionItem", &obpidl, &pt.x, &pt.y, &dwFlags)) + return NULL; + if (!toPIDL(obpidl, &pidl, pIFV)) + return NULL; + PY_INTERFACE_PRECALL; + HRESULT hr = pIFV->SelectAndPositionItems(1, (LPCITEMIDLIST *)&pidl, &pt, dwFlags); + PyObject_FreePIDL(pidl); + PY_INTERFACE_POSTCALL; + if (FAILED(hr)) + return PyCom_BuildPyException(hr, pIFV, IID_IFolderView); + Py_RETURN_NONE; +} + +// @object PyIFolderView|Description of the interface +static struct PyMethodDef PyIFolderView_methods[] = { + {"GetCurrentViewMode", PyIFolderView::GetCurrentViewMode, + 1}, // @pymeth GetCurrentViewMode|Description of GetCurrentViewMode + {"SetCurrentViewMode", PyIFolderView::SetCurrentViewMode, + 1}, // @pymeth SetCurrentViewMode|Description of SetCurrentViewMode + {"GetFolder", PyIFolderView::GetFolder, 1}, // @pymeth GetFolder|Description of GetFolder + {"Item", PyIFolderView::Item, 1}, // @pymeth Item|Description of Item + {"ItemCount", PyIFolderView::ItemCount, 1}, // @pymeth ItemCount|Description of ItemCount + {"Items", PyIFolderView::Items, 1}, // @pymeth Items|Description of Items + {"GetSelectionMarkedItem", PyIFolderView::GetSelectionMarkedItem, + 1}, // @pymeth GetSelectionMarkedItem|Description of GetSelectionMarkedItem + {"GetFocusedItem", PyIFolderView::GetFocusedItem, 1}, // @pymeth GetFocusedItem|Description of GetFocusedItem + {"GetItemPosition", PyIFolderView::GetItemPosition, 1}, // @pymeth GetItemPosition|Description of GetItemPosition + {"GetSpacing", PyIFolderView::GetSpacing, 1}, // @pymeth GetSpacing|Description of GetSpacing + {"GetDefaultSpacing", PyIFolderView::GetDefaultSpacing, + 1}, // @pymeth GetDefaultSpacing|Description of GetDefaultSpacing + {"GetAutoArrange", PyIFolderView::GetAutoArrange, 1}, // @pymeth GetAutoArrange|Description of GetAutoArrange + {"SelectItem", PyIFolderView::SelectItem, 1}, // @pymeth SelectItem|Description of SelectItem + {"SelectAndPositionItems", PyIFolderView::SelectAndPositionItems, + 1}, // @pymeth SelectAndPositionItems|Description of SelectAndPositionItems + {"SelectAndPositionItem", PyIFolderView::SelectAndPositionItem, + 1}, // @pymeth SelectAndPositionItem|Description of SelectAndPositionItem + {NULL}}; + +PyComTypeObject PyIFolderView::type("PyIFolderView", &PyIUnknown::type, sizeof(PyIFolderView), PyIFolderView_methods, + GET_PYCOM_CTOR(PyIFolderView)); diff --git a/com/win32comext/shell/src/PyIFolderView.h b/com/win32comext/shell/src/PyIFolderView.h new file mode 100644 index 0000000000..8dd85384bf --- /dev/null +++ b/com/win32comext/shell/src/PyIFolderView.h @@ -0,0 +1,33 @@ +// This file declares the IFolderView Interface for Python. +// Generated by makegw.py +// --------------------------------------------------- +// +// Interface Declaration + +class PyIFolderView : public PyIUnknown { + public: + MAKE_PYCOM_CTOR(PyIFolderView); + static IFolderView *GetI(PyObject *self); + static PyComTypeObject type; + + // The Python methods + static PyObject *GetCurrentViewMode(PyObject *self, PyObject *args); + static PyObject *SetCurrentViewMode(PyObject *self, PyObject *args); + static PyObject *GetFolder(PyObject *self, PyObject *args); + static PyObject *Item(PyObject *self, PyObject *args); + static PyObject *ItemCount(PyObject *self, PyObject *args); + static PyObject *Items(PyObject *self, PyObject *args); + static PyObject *GetSelectionMarkedItem(PyObject *self, PyObject *args); + static PyObject *GetFocusedItem(PyObject *self, PyObject *args); + static PyObject *GetItemPosition(PyObject *self, PyObject *args); + static PyObject *GetSpacing(PyObject *self, PyObject *args); + static PyObject *GetDefaultSpacing(PyObject *self, PyObject *args); + static PyObject *GetAutoArrange(PyObject *self, PyObject *args); + static PyObject *SelectItem(PyObject *self, PyObject *args); + static PyObject *SelectAndPositionItems(PyObject *self, PyObject *args); + static PyObject *SelectAndPositionItem(PyObject *self, PyObject *args); + + protected: + PyIFolderView(IUnknown *pdisp); + ~PyIFolderView(); +}; diff --git a/com/win32comext/shell/src/shell.cpp b/com/win32comext/shell/src/shell.cpp index 1c5b15f43c..09a48dab9f 100644 --- a/com/win32comext/shell/src/shell.cpp +++ b/com/win32comext/shell/src/shell.cpp @@ -74,6 +74,7 @@ generates Windows .hlp files. #include "PyITransferAdviseSink.h" #include "PyIShellItemResources.h" #include "PyIEnumResources.h" +#include "PyIFolderView.h" #include "PyIRelatedItem.h" // Next 4 all derived from IRelatedItem #include "PyIDisplayItem.h" @@ -595,9 +596,7 @@ BOOL PyObject_AsCMINVOKECOMMANDINFO(PyObject *ob, CMINVOKECOMMANDINFO *pci) return FALSE; return TRUE; } -void PyObject_FreeCMINVOKECOMMANDINFO(CMINVOKECOMMANDINFO *pci) { - PyWinObject_FreeResourceIdA((char *)pci->lpVerb); -} +void PyObject_FreeCMINVOKECOMMANDINFO(CMINVOKECOMMANDINFO *pci) { PyWinObject_FreeResourceIdA((char *)pci->lpVerb); } static PyObject *PyString_FromMaybeNullString(const char *sz) { @@ -947,12 +946,12 @@ BOOL PyObject_AsSHFILEOPSTRUCT(PyObject *ob, SHFILEOPSTRUCT *p) memset(p, 0, sizeof(*p)); if (!PyArg_ParseTuple( ob, "OiOO|iOO", - &obhwnd, // @tupleitem 0|int|hwnd|Handle of window in which to display status messages - &p->wFunc, // @tupleitem 1|int|wFunc|One of the shellcon.FO_* values - &obFrom, // @tupleitem 2|string|From|String containing source file name(s) separated by nulls - &obTo, // @tupleitem 3|string|To|String containing destination file name(s) separated by nulls, can be - // None - &p->fFlags, // @tupleitem 4|int|flags|Combination of shellcon.FOF_* flags. Default=0 + &obhwnd, // @tupleitem 0|int|hwnd|Handle of window in which to display status messages + &p->wFunc, // @tupleitem 1|int|wFunc|One of the shellcon.FO_* values + &obFrom, // @tupleitem 2|string|From|String containing source file name(s) separated by nulls + &obTo, // @tupleitem 3|string|To|String containing destination file name(s) separated by nulls, can be + // None + &p->fFlags, // @tupleitem 4|int|flags|Combination of shellcon.FOF_* flags. Default=0 &obNameMappings, // @tupleitem 5|None|NameMappings|Maps input file names to their new names. This is // actually output, and must be None if passed as input. Default=None &obProgressTitle)) // @tupleitem 6|string|ProgressTitle|Title for progress dialog (flags must contain @@ -1275,16 +1274,20 @@ static PyObject *PySHGetSpecialFolderPath(PyObject *self, PyObject *args) return PyWinObject_FromWCHAR(buf); } -// @pymethod string|shell|SHGetKnownFolderPath|Retrieves the full path of a known folder identified by the folder's KNOWNFOLDERID. +// @pymethod string|shell|SHGetKnownFolderPath|Retrieves the full path of a known folder identified by the folder's +// KNOWNFOLDERID. static PyObject *PySHGetKnownFolderPath(PyObject *self, PyObject *args) { PyObject *obfid; PyObject *obHandle = Py_None; long flags = 0; if (!PyArg_ParseTuple(args, "O|lO:SHGetKnownFolderPath", - &obfid, // @pyparm |fid||One of the KNOWNFOLDERID constants. - &flags, // @pyparm int|flags|0|Flags that specify special retrieval options. This value can be 0; otherwise, one or more of the KNOWN_FOLDER_FLAG values. - &obHandle)) // @pyparm |token|None|An access token that represents a particular user. If this parameter is NULL, which is the most common usage, the function requests the known folder for the current user. + &obfid, // @pyparm |fid||One of the KNOWNFOLDERID constants. + &flags, // @pyparm int|flags|0|Flags that specify special retrieval options. This value can + // be 0; otherwise, one or more of the KNOWN_FOLDER_FLAG values. + &obHandle)) // @pyparm |token|None|An access token that represents a particular + // user. If this parameter is NULL, which is the most common usage, the function + // requests the known folder for the current user. return NULL; KNOWNFOLDERID fid; if (!PyWinObject_AsIID(obfid, &fid)) @@ -3651,7 +3654,8 @@ static struct PyMethodDef shell_methods[] = { 1}, // @pymeth SHGetSpecialFolderPath|Retrieves the path of a special folder. {"SHGetSpecialFolderLocation", PySHGetSpecialFolderLocation, 1}, // @pymeth SHGetSpecialFolderLocation|Retrieves the of a special folder. - {"SHGetKnownFolderPath", PySHGetKnownFolderPath, 1}, // @pymeth SHGetKnownFolderPath|Retrieves the full path of a known folder identified by the folder's KNOWNFOLDERID. + {"SHGetKnownFolderPath", PySHGetKnownFolderPath, 1}, // @pymeth SHGetKnownFolderPath|Retrieves the full path of a + // known folder identified by the folder's KNOWNFOLDERID. {"SHAddToRecentDocs", PySHAddToRecentDocs, 1}, // @pymeth SHAddToRecentDocs|Adds a document to the shell's list of recently used documents or clears all // documents from the list. The user gains access to the list through the Start menu of the Windows taskbar. @@ -3807,6 +3811,7 @@ static const PyCom_InterfaceSupportInfo g_interfaceSupportData[] = { PYCOM_INTERFACE_FULL(TransferAdviseSink), PYCOM_INTERFACE_FULL(ShellItemResources), PYCOM_INTERFACE_FULL(EnumResources), + PYCOM_INTERFACE_CLIENT_ONLY(FolderView), PYCOM_INTERFACE_FULL(RelatedItem), PYCOM_INTERFACE_FULL(TransferMediumItem), // based on IRelatedItem with no extra methods PYCOM_INTERFACE_FULL(CurrentItem), // based on IRelatedItem with no extra methods diff --git a/isapi/install.py b/isapi/install.py index f28cf2ea0c..b89c521dfa 100644 --- a/isapi/install.py +++ b/isapi/install.py @@ -2,6 +2,8 @@ # this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache # Created July 2004, Mark Hammond. +from __future__ import annotations + import importlib.machinery import os import shutil @@ -73,7 +75,7 @@ class VirtualDirParameters: EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING EnableDefaultDoc = _DEFAULT_ENABLE_DEFAULT_DOC DefaultDoc = None # Only set in IIS if not None - ScriptMaps = [] + ScriptMaps: list[ScriptMapParams] = [] ScriptMapUpdate = "end" # can be 'start', 'end', 'replace' Server = None @@ -117,8 +119,8 @@ def __str__(self): class ISAPIParameters: ServerName = _DEFAULT_SERVER_NAME # Description = None - Filters = [] - VirtualDirs = [] + Filters: list[FilterParameters] = [] + VirtualDirs: list[VirtualDirParameters] = [] def __init__(self, **kw): self.__dict__.update(kw) diff --git a/isapi/isapicon.py b/isapi/isapicon.py index 20de1a44c2..d4c16a5459 100644 --- a/isapi/isapicon.py +++ b/isapi/isapicon.py @@ -1,4 +1,5 @@ """Constants needed by ISAPI filters and extensions.""" + # ====================================================================== # Copyright 2002-2003 by Blackdog Software Pty Ltd. # diff --git a/isapi/samples/advanced.py b/isapi/samples/advanced.py index 913de024a6..3cce5d885b 100644 --- a/isapi/samples/advanced.py +++ b/isapi/samples/advanced.py @@ -67,7 +67,7 @@ from isapi import InternalReloadException try: - reload_counter += 1 + reload_counter += 1 # type: ignore[used-before-def] except NameError: reload_counter = 0 diff --git a/isapi/simple.py b/isapi/simple.py index b453bbae01..d5fcb04d7c 100644 --- a/isapi/simple.py +++ b/isapi/simple.py @@ -8,6 +8,8 @@ must ensure each of the required methods are implemented. """ +from __future__ import annotations + class SimpleExtension: "Base class for a simple ISAPI extension" @@ -38,7 +40,7 @@ def TerminateExtension(self, status): class SimpleFilter: "Base class for a a simple ISAPI filter" - filter_flags = None + filter_flags: int | None = None def __init__(self): pass diff --git a/isapi/threaded_extension.py b/isapi/threaded_extension.py index 12a1191df6..ecf24c8460 100644 --- a/isapi/threaded_extension.py +++ b/isapi/threaded_extension.py @@ -1,4 +1,5 @@ """An ISAPI extension base class implemented using a thread-pool.""" + # $Id$ import sys @@ -165,10 +166,11 @@ def HandleDispatchError(self, ecb): list = traceback.format_tb( exc_tb, limit ) + traceback.format_exception_only(exc_typ, exc_val) + bold = list.pop() print( "
{}{}
".format( - cgi.escape("".join(list[:-1])), - cgi.escape(list[-1]), + cgi.escape("".join(list)), + cgi.escape(bold), ), file=ecb, ) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000000..9055b84925 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,38 @@ +[mypy] +show_column_numbers = true +warn_unused_ignores = true +; Target the oldest supported version in editors +python_version = 3.7 + +strict = false +implicit_reexport = true + +; 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 +check_untyped_defs = false +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) + +; C-modules that will need type-stubs +[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] +ignore_missing_imports = True + +; verstamp is installed from win32verstamp.py called in setup.py +; Most of win32com re-exports win32comext +; Test is a local untyped module in win32comext.axdebug +; pywin32_system32 is an empty module created in setup.py to store dlls +[mypy-verstamp,win32com.*,Test,pywin32_system32] +ignore_missing_imports = True diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000000..1dd371af23 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,60 @@ +{ + "typeCheckingMode": "basic", + // Target the oldest supported version in editors + "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 + "Pythonwin/pywin/idle/", + // Ignoring non-public apis for now + "**/Test/", + "**/test/", + "**/Demos/", + "**/demo/", + ], + // 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 + "extraPaths": [ + "com", + "win32/Lib", + "Pythonwin", + ], + // 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", + // FIXE: These all need to be fixed first and turned back to error + // some of the fixes need to be done in types-pywin32 from typeshed + "reportAssignmentType": "warning", + "reportCallIssue": "warning", + "reportIndexIssue": "warning", + "reportOperatorIssue": "warning", + "reportOptionalCall": "warning", + "reportOptionalIterable": "warning", + "reportOptionalMemberAccess": "warning", + "reportOptionalSubscript": "warning", + // TODO: Leave Unbound/Undefined to its own PR(s) + "reportUnboundVariable": "warning", + "reportUndefinedVariable": "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. + // TODO: Is intended to be fixed with an editable install + // Since we're a library, and not user code, we care less about forgetting to install a dependency, + // as long as we have its stubs. So just disabling for now is fine. + "reportMissingModuleSource": "none", + // External type stubs may not be completable, and this will require type stubs for dynamic modules. + "reportMissingTypeStubs": "information", + // Sometimes used for extra runtime safety + "reportUnnecessaryComparison": "warning", + // Use Flake8/Pycln/Ruff instead + "reportUnusedImport": "none", +} diff --git a/pywin32_postinstall.py b/pywin32_postinstall.py index 0662da56bf..5a4981edc6 100644 --- a/pywin32_postinstall.py +++ b/pywin32_postinstall.py @@ -1,20 +1,15 @@ # postinstall script for pywin32 # -# copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory, +# copies pywintypesXX.dll and pythoncomXX.dll into the system directory, # and creates a pth file +import argparse import glob import os import shutil import sys import sysconfig - -try: - import winreg as winreg -except: - import winreg - -# Send output somewhere so it can be found if necessary... -import tempfile +import tempfile # Send output somewhere so it can be found if necessary... +import winreg tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w") @@ -44,11 +39,11 @@ def flush(self): # with sys.stdout as None but stderr is hooked up. This work-around allows # bdist_wininst to see the output we write and display it at the end of # the install. -if sys.stdout is None: +if sys.stdout is None: # pyright: ignore[reportUnnecessaryComparison] sys.stdout = sys.stderr -sys.stderr = Tee(sys.stderr) -sys.stdout = Tee(sys.stdout) +sys.stderr = Tee(sys.stderr) # type: ignore[assignment] # Not an actual TextIO +sys.stdout = Tee(sys.stdout) # type: ignore[assignment] # Not an actual TextIO com_modules = [ # module_name, class_names @@ -72,7 +67,7 @@ def flush(self): # functions which write lines to PythonXX\pywin32-install.log. This is # a list of actions for the uninstaller, the format is inspired by what # the Wise installer also creates. - file_created + file_created # type: ignore[used-before-def] # 3.10 stopped supporting bdist_wininst, but we can still build them with 3.9. # This can be kept until Python 3.9 or exe installers support is dropped. is_bdist_wininst = True @@ -98,7 +93,7 @@ def get_root_hkey(): try: - create_shortcut + create_shortcut # type: ignore[used-before-def] except NameError: # Create a function with the same signature as create_shortcut provided # by bdist_wininst @@ -193,7 +188,9 @@ 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(mod) + spec.loader.exec_module( # pyright: ignore[reportOptionalMemberAccess] # We provide the loader, we know it won't be None + mod + ) def SetPyKeyVal(key_name, value_name, value): @@ -697,8 +694,6 @@ def verify_destination(location): def main(): - import argparse - parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="""A post-install script for the pywin32 extensions. diff --git a/pywin32_testall.py b/pywin32_testall.py index e6c4b7c8c6..9071549174 100644 --- a/pywin32_testall.py +++ b/pywin32_testall.py @@ -1,4 +1,5 @@ """A test runner for pywin32""" + import os import site import subprocess diff --git a/setup.py b/setup.py index 67d9396c59..4181b0f4d0 100644 --- a/setup.py +++ b/setup.py @@ -44,11 +44,12 @@ from setuptools.command.install import install from setuptools.command.install_lib import install_lib -# https://github.com/pypa/setuptools/pull/4069 -from distutils.dep_util import newer_group from distutils.command.install_data import install_data -from distutils.command.install_lib import install_lib -from distutils.core import Extension + +if sys.version_info >= (3, 8): + from setuptools.modified import newer_group +else: + from distutils.dep_util import newer_group # some modules need a static CRT to avoid problems caused by them having a # manifest. @@ -200,10 +201,7 @@ def finalize_options(self, build_ext): # If someone needs a specially named implib created, handle that if self.implib_name: implib = os.path.join(build_ext.build_temp, self.implib_name) - if build_ext.debug: - suffix = "_d" - else: - suffix = "" + suffix = "_d" if build_ext.debug else "" self.extra_link_args.append(f"/IMPLIB:{implib}{suffix}.lib") # Try and find the MFC headers, so we can reach inside for # some of the ActiveX support we need. We need to do this late, so @@ -626,9 +624,7 @@ def build_extensions(self): target_dir = os.path.join(self.build_lib, clib_file[0], "libs") if not os.path.exists(target_dir): self.mkpath(target_dir) - suffix = "" - if self.debug: - suffix = "_d" + suffix = "_d" if self.debug else "" fname = clib_file[1] % suffix self.copy_file(os.path.join(self.build_temp, fname), target_dir) @@ -660,10 +656,9 @@ def build_extensions(self): self.copy_file(mfc_content, target_dir) def build_exefile(self, ext): - _d = self.debug and "_d" or "" - + suffix = "_d" if self.debug else "" logging.info("building exe '%s'", ext.name) - leaf_name = f"{ext.get_pywin32_dir()}\\{ext.name}{_d}.exe" + leaf_name = f"{ext.get_pywin32_dir()}\\{ext.name}{suffix}.exe" full_name = os.path.join(self.build_lib, leaf_name) sources = list(ext.sources) @@ -745,7 +740,7 @@ def build_extension(self, ext): # Convincing distutils to create .lib files with the name we # need is difficult, so we just hack around it by copying from # the created name to the name we need. - extra = self.debug and "_d.lib" or ".lib" + extra = "_d.lib" if self.debug else ".lib" if ext.name in ("pywintypes", "pythoncom"): # The import libraries are created as PyWinTypes23.lib, but # are expected to be pywintypes.lib. @@ -782,15 +777,15 @@ def build_extension(self, ext): def get_ext_filename(self, name): # We need to fixup some target filenames. - _d = self.debug and "_d" or "" + suffix = "_d" if self.debug else "" if name in ["pywintypes", "pythoncom"]: ver = f"{sys.version_info[0]}{sys.version_info[1]}" - return f"{name}{ver}{_d}.dll" + return f"{name}{ver}{suffix}.dll" if name in ["perfmondata", "PyISAPI_loader"]: - return f"{name}{_d}.dll" + return f"{name}{suffix}.dll" # everything else a .pyd - calling base-class might give us a more # complicated name, so return a simple one. - return f"{name}{_d}.pyd" + return f"{name}{suffix}.pyd" def get_export_symbols(self, ext): if ext.is_regular_dll: @@ -879,7 +874,7 @@ def swig_sources(self, sources, ext=None): # A class deriving from other than the default swig_cmd.extend(["-com_interface_parent", interface_parent]) - # This 'newer' check helps python 2.2 builds, which otherwise + # This 'newer' check helps Python 2.2 builds, which otherwise # *always* regenerate the .cpp files, meaning every future # build for any platform sees these as dirty. # This could probably go once we generate .cpp into the temp dir. @@ -1648,7 +1643,7 @@ def finalize_options(self): {mapi}/PyIMsgStore.i {mapi}/PyIMsgStore.cpp {mapi}/PyIProfAdmin.i {mapi}/PyIProfAdmin.cpp {mapi}/PyIProfSect.i {mapi}/PyIProfSect.cpp - {mapi}/PyIConverterSession.i {mapi}/PyIConverterSession.cpp + {mapi}/PyIConverterSession.i {mapi}/PyIConverterSession.cpp {mapi}/PyIMAPIAdviseSink.cpp {mapi}/mapiutil.cpp {mapi}/mapiguids.cpp @@ -1734,6 +1729,7 @@ def finalize_options(self): {shell}/PyIExtractImage.cpp {shell}/PyIFileOperation.cpp {shell}/PyIFileOperationProgressSink.cpp + {shell}/PyIFolderView.cpp {shell}/PyIIdentityName.cpp {shell}/PyIInputObject.cpp {shell}/PyIKnownFolder.cpp diff --git a/win32/Demos/c_extension/win32_extension.cpp b/win32/Demos/c_extension/win32_extension.cpp index 8768424e39..eb1c91d531 100644 --- a/win32/Demos/c_extension/win32_extension.cpp +++ b/win32/Demos/c_extension/win32_extension.cpp @@ -1,6 +1,6 @@ // Note: this sample does nothing useful other than to show you how // your own Python extension can link with and use the functions from -// pywintypesxx.dll +// pywintypesXX.dll #include "Python.h" #include "PyWinTypes.h" diff --git a/win32/Demos/security/sspi/socket_server.py b/win32/Demos/security/sspi/socket_server.py index d1ac70b22c..6bf3806bb4 100644 --- a/win32/Demos/security/sspi/socket_server.py +++ b/win32/Demos/security/sspi/socket_server.py @@ -19,7 +19,6 @@ See the SSPI documentation for more details. """ - import http.client # sorry, this demo needs 2.3+ import optparse import socketserver diff --git a/win32/Demos/win32cred_demo.py b/win32/Demos/win32cred_demo.py index 3b49ca7c8f..c51cc7262a 100644 --- a/win32/Demos/win32cred_demo.py +++ b/win32/Demos/win32cred_demo.py @@ -47,7 +47,7 @@ mc = win32cred.CredMarshalCredential(win32cred.UsernameTargetCredential, target) # As of pywin32 301 this no longer works for markh and unclear when it stopped, or -# even if it ever did! # Fails in Python 2.7 too, so not a 3.x regression. +# even if it ever did! # Fails in Python 2.7 too, so not a Python 3 regression. try: th = win32security.LogonUser( mc, diff --git a/win32/Demos/win32ts_logoff_disconnected.py b/win32/Demos/win32ts_logoff_disconnected.py index be61e5d1f3..2a8bcc9403 100644 --- a/win32/Demos/win32ts_logoff_disconnected.py +++ b/win32/Demos/win32ts_logoff_disconnected.py @@ -1,4 +1,5 @@ """ Finds any disconnected terminal service sessions and logs them off""" + import pywintypes import win32ts import winerror diff --git a/win32/Lib/pywin32_testutil.py b/win32/Lib/pywin32_testutil.py index d512404972..4dc33d1ae8 100644 --- a/win32/Lib/pywin32_testutil.py +++ b/win32/Lib/pywin32_testutil.py @@ -207,16 +207,9 @@ class TestSkipped(Exception): pass -# This appears to have been "upgraded" to non-private in 3.11 -try: - TextTestResult = unittest._TextTestResult -except AttributeError: - TextTestResult = unittest.TextTestResult - - # The 'TestResult' subclass that records the failures and has the special # handling for the TestSkipped exception. -class TestResult(TextTestResult): +class TestResult(unittest.TextTestResult): def __init__(self, *args, **kw): super().__init__(*args, **kw) self.skips = {} # count of skips for each reason. diff --git a/win32/Lib/pywintypes.py b/win32/Lib/pywintypes.py index 6839664f2c..14f9407c5a 100644 --- a/win32/Lib/pywintypes.py +++ b/win32/Lib/pywintypes.py @@ -1,4 +1,4 @@ -# Magic utility that "redirects" to pywintypesxx.dll +# Magic utility that "redirects" to pywintypesXX.dll import importlib.machinery import importlib.util import os @@ -56,7 +56,7 @@ def __import_pywin32_system_module__(modname, globs): # first (which is how we are here) or if, eg, win32api was imported # first thereby implicitly loading the DLL. - # Sadly though, it doesn't quite work - if pywintypesxx.dll + # Sadly though, it doesn't quite work - if pywintypesXX.dll # is in system32 *and* the executable's directory, on XP SP2, an # import of win32api will cause Windows to load pywintypes # from system32, where LoadLibrary for that name will diff --git a/win32/Lib/sspi.py b/win32/Lib/sspi.py index 657577831d..79afa58393 100644 --- a/win32/Lib/sspi.py +++ b/win32/Lib/sspi.py @@ -11,6 +11,7 @@ If you need finer control than offered here, just use the win32security functions directly. """ + # Based on Roger Upole's sspi demos. # $Id$ import sspicon @@ -371,7 +372,7 @@ def authorize(self, sec_buffer_in): sec_buffer = None client_step = 0 server_step = 0 - while not (sspiclient.authenticated) or len(sec_buffer[0].Buffer): + while not sspiclient.authenticated or (sec_buffer and len(sec_buffer[0].Buffer)): client_step += 1 err, sec_buffer = sspiclient.authorize(sec_buffer) print("Client step %s" % client_step) diff --git a/win32/Lib/win32con.py b/win32/Lib/win32con.py index 9916ba4aba..5e798ee293 100644 --- a/win32/Lib/win32con.py +++ b/win32/Lib/win32con.py @@ -23,7 +23,7 @@ OFN_NOLONGNAMES = 262144 OFN_EXPLORER = 524288 # new look commdlg OFN_NODEREFERENCELINKS = 1048576 -OFN_LONGNAMES = 2097152 # force long names for 3.x modules +OFN_LONGNAMES = 2097152 # force long names for Python 3 modules OFN_ENABLEINCLUDENOTIFY = 4194304 # send include message to callback OFN_ENABLESIZING = 8388608 OFN_DONTADDTORECENT = 33554432 diff --git a/win32/Lib/win32pdhquery.py b/win32/Lib/win32pdhquery.py index a48db08a83..bda1665f0a 100644 --- a/win32/Lib/win32pdhquery.py +++ b/win32/Lib/win32pdhquery.py @@ -17,37 +17,37 @@ complexity from the end-user/programmer. EXAMPLE: A more complex Path - '\\\\RAISTLIN\\PhysicalDisk(_Total)\\Avg. Disk Bytes/Read' - Raistlin --> Computer Name - PhysicalDisk --> Object Name - _Total --> The particular Instance (in this case, all instances, i.e. all drives) - Avg. Disk Bytes/Read --> The piece of data being monitored. + '\\\\RAISTLIN\\PhysicalDisk(_Total)\\Avg. Disk Bytes/Read' + Raistlin --> Computer Name + PhysicalDisk --> Object Name + _Total --> The particular Instance (in this case, all instances, i.e. all drives) + Avg. Disk Bytes/Read --> The piece of data being monitored. EXAMPLE: Collecting Data with a Query - As an example, the following code implements a logger which allows the - user to choose what counters they would like to log, and logs those - counters for 30 seconds, at two-second intervals. - - query = Query() - query.addcounterbybrowsing() - query.collectdatafor(30,2) - - The data is now stored in a list of lists as: - query.curresults - - The counters(paths) which were used to collect the data are: - query.curpaths - - You can use the win32pdh.ParseCounterPath(path) utility function - to turn the paths into more easily read values for your task, or - write the data to a file, or do whatever you want with it. + As an example, the following code implements a logger which allows the + user to choose what counters they would like to log, and logs those + counters for 30 seconds, at two-second intervals. + + query = Query() + query.addcounterbybrowsing() + query.collectdatafor(30,2) + + The data is now stored in a list of lists as: + query.curresults + + The counters(paths) which were used to collect the data are: + query.curpaths + + You can use the win32pdh.ParseCounterPath(path) utility function + to turn the paths into more easily read values for your task, or + write the data to a file, or do whatever you want with it. OTHER NOTABLE METHODS: - query.collectdatawhile(period) # start a logging thread for collecting data - query.collectdatawhile_stop() # signal the logging thread to stop logging - query.collectdata() # run the query only once - query.addperfcounter(object, counter, machine=None) # add a standard performance counter - query.addinstcounter(object, counter,machine=None,objtype = 'Process',volatile=1,format = win32pdh.PDH_FMT_LONG) # add a possibly volatile counter + query.collectdatawhile(period) # start a logging thread for collecting data + query.collectdatawhile_stop() # signal the logging thread to stop logging + query.collectdata() # run the query only once + query.addperfcounter(object, counter, machine=None) # add a standard performance counter + query.addinstcounter(object, counter,machine=None,objtype = 'Process',volatile=1,format = win32pdh.PDH_FMT_LONG) # add a possibly volatile counter ### Known bugs and limitations ### Due to a problem with threading under the PythonWin interpreter, there @@ -123,6 +123,7 @@ if you use it, you accept the risk of using it, etceteras. """ + # Feb 12, 98 - MH added "rawaddcounter" so caller can get exception details. import _thread @@ -495,7 +496,7 @@ def collectdatafor(self, totalperiod, period=1): tempresults = [] try: self.open() - for ind in range(totalperiod / period): + for ind in range(int(totalperiod / period)): tempresults.append(self.collectdata()) time.sleep(period) self.curresults = tempresults diff --git a/win32/Lib/win32pdhutil.py b/win32/Lib/win32pdhutil.py index 7795dd904e..850e0ab89d 100644 --- a/win32/Lib/win32pdhutil.py +++ b/win32/Lib/win32pdhutil.py @@ -18,6 +18,8 @@ the easiest way is often to simply use PerfMon to find out the names. """ +from __future__ import annotations + import time import win32pdh @@ -27,7 +29,7 @@ # Handle some localization issues. # see http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q287/1/59.asp&NoWebContent=1 # Build a map of english_counter_name: counter_id -counter_english_map = {} +counter_english_map: dict[str, int] = {} def find_pdh_counter_localized_name(english_name, machine_name=None): diff --git a/win32/Lib/win32rcparser.py b/win32/Lib/win32rcparser.py index 9b909898ec..8f065bb8eb 100644 --- a/win32/Lib/win32rcparser.py +++ b/win32/Lib/win32rcparser.py @@ -7,8 +7,7 @@ This is a parser for Windows .rc files, which are text files which define dialogs and other Windows UI resources. """ -__author__ = "Adam Walker" -__version__ = "0.11" +from __future__ import annotations import os import pprint @@ -19,6 +18,9 @@ import commctrl import win32con +__author__ = "Adam Walker" +__version__ = "0.11" + _controlMap = { "DEFPUSHBUTTON": 0x80, "PUSHBUTTON": 0x80, @@ -171,8 +173,8 @@ def __repr__(self): class RCParser: next_id = 1001 - dialogs = {} - _dialogs = {} + dialogs: dict[str, list[list[str | int | None | tuple[str | int, ...]]]] = {} + _dialogs: dict[str, DialogDef] = {} debugEnabled = False token = "" @@ -654,7 +656,7 @@ def GenerateFrozenResource(rc_name, output_name, h_name=None): else: filename = sys.argv[1] if "-v" in sys.argv: - RCParser.debugEnabled = 1 + RCParser.debugEnabled = True print("Dumping all resources in '%s'" % filename) resources = Parse(filename) for id, ddef in resources.dialogs.items(): diff --git a/win32/Lib/win32serviceutil.py b/win32/Lib/win32serviceutil.py index 16de6814c5..fd4be7a5e9 100644 --- a/win32/Lib/win32serviceutil.py +++ b/win32/Lib/win32serviceutil.py @@ -17,7 +17,6 @@ import win32service import winerror -_d = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" error = RuntimeError @@ -35,9 +34,11 @@ def LocatePythonServiceExe(exe=None): if exe and os.path.isfile(exe): return win32api.GetFullPathName(exe) + suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" + # We are confused if we aren't now looking for our default. But if that # exists as specified we assume it's good. - exe = f"pythonservice{_d}.exe" + exe = f"pythonservice{suffix}.exe" if os.path.isfile(exe): return win32api.GetFullPathName(exe) @@ -62,7 +63,7 @@ def LocatePythonServiceExe(exe=None): # (Unlike the .exe above, we don't unconditionally copy this, and possibly # copy it to a different place. Doesn't seem a good reason for that!?) python_dll = win32api.GetModuleFileName(sys.dllhandle) - pyw = f"pywintypes{sys.version_info[0]}{sys.version_info[1]}{_d}.dll" + pyw = f"pywintypes{sys.version_info[0]}{sys.version_info[1]}{suffix}.dll" correct_pyw = os.path.join(os.path.dirname(python_dll), pyw) if not os.path.exists(correct_pyw): @@ -441,8 +442,6 @@ def ControlService(serviceName, code, machine=None): def __FindSvcDeps(findName): - if isinstance(findName, pywintypes.UnicodeType): - findName = str(findName) dict = {} k = win32api.RegOpenKey( win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services" diff --git a/win32/Lib/win32timezone.py b/win32/Lib/win32timezone.py index 8af89c1df5..dc41514e03 100644 --- a/win32/Lib/win32timezone.py +++ b/win32/Lib/win32timezone.py @@ -231,7 +231,7 @@ datetime.datetime(2011, 11, 6, 1, 0, tzinfo=TimeZoneInfo('Pacific Standard Time')) """ -__author__ = "Jason R. Coombs " +from __future__ import annotations import datetime import logging @@ -243,13 +243,15 @@ import win32api +__author__ = "Jason R. Coombs " + log = logging.getLogger(__file__) # A couple of objects for working with objects as if they were native C-type # structures. class _SimpleStruct: - _fields_ = None # must be overridden by subclasses + _fields_: list[tuple[str, type]] = [] # must be overridden by subclasses def __init__(self, *args, **kw): for i, (name, typ) in enumerate(self._fields_): diff --git a/win32/Lib/winerror.py b/win32/Lib/winerror.py index d7697dbd65..e9e047a12f 100644 --- a/win32/Lib/winerror.py +++ b/win32/Lib/winerror.py @@ -2,6 +2,7 @@ Generated by h2py from winerror.h """ + # Few extras added manually... TRUST_E_PROVIDER_UNKNOWN = -2146762751 TRUST_E_ACTION_UNKNOWN = -2146762750 diff --git a/win32/Lib/winxptheme.py b/win32/Lib/winxptheme.py index 3f61c3de58..e147eda58f 100644 --- a/win32/Lib/winxptheme.py +++ b/win32/Lib/winxptheme.py @@ -4,4 +4,5 @@ be available. In 2022, it's safe to assume they are, so this is just a wrapper around _winxptheme. """ + from _winxptheme import * diff --git a/win32/scripts/VersionStamp/BrandProject.py b/win32/scripts/VersionStamp/BrandProject.py index 2612dbc94b..9fc96033c8 100644 --- a/win32/scripts/VersionStamp/BrandProject.py +++ b/win32/scripts/VersionStamp/BrandProject.py @@ -62,11 +62,12 @@ def usage(msg): if __name__ == "__main__": + import getopt + try: - import getopt opts, args = getopt.getopt(sys.argv[1:], "af:d:r") - except getopts.error as msg: + except getopt.GetoptError as msg: usage(msg) bAuto = bRebrand = 0 stampFiles = [] diff --git a/win32/scripts/VersionStamp/bulkstamp.py b/win32/scripts/VersionStamp/bulkstamp.py index 43f2342d43..81f7cbcd53 100644 --- a/win32/scripts/VersionStamp/bulkstamp.py +++ b/win32/scripts/VersionStamp/bulkstamp.py @@ -56,7 +56,7 @@ def walk(arg, dirname, names): # Handle the "_d" thing. pathname = os.path.join(dirname, name) base, ext = os.path.splitext(name) - if base[-2:] == "_d": + if base.endswith("_d"): name = base[:-2] + ext is_dll = ext.lower() != ".exe" if os.path.normcase(name) in descriptions: diff --git a/win32/scripts/ce/pysynch.py b/win32/scripts/ce/pysynch.py index 3244ae45dc..f01e7e4b89 100644 --- a/win32/scripts/ce/pysynch.py +++ b/win32/scripts/ce/pysynch.py @@ -4,6 +4,7 @@ import getopt import os import sys +from collections.abc import Callable import win32api import win32con diff --git a/win32/scripts/regsetup.py b/win32/scripts/regsetup.py index 6d9ff6675c..da2f870e1d 100644 --- a/win32/scripts/regsetup.py +++ b/win32/scripts/regsetup.py @@ -503,7 +503,7 @@ def RegisterShellInfo(searchPaths): "regsetup c:\wierd\spot\1 c:\wierd\spot\2" Attempts to setup the core Python. Looks in some standard places, as well as the 2 wierd spots to locate the core Python files (eg, Python.exe, -pythonXX.dll, the standard library and Win32 Extensions. +pythonXX.dll, the standard library and Win32 Extensions). "regsetup -a myappname . .\subdir" Registers a new Pythonpath entry named myappname, with "C:\I\AM\HERE" and diff --git a/win32/scripts/setup_d.py b/win32/scripts/setup_d.py index caf10f9a7c..c40cd65c87 100644 --- a/win32/scripts/setup_d.py +++ b/win32/scripts/setup_d.py @@ -1,4 +1,4 @@ -# Install and register pythonXX_d.dll, pywintypesxx_d.dll and pythoncomxx_d.dll +# Install and register pythonXX_d.dll, pywintypesXX_d.dll and pythoncomXX_d.dll # # Assumes the _d files can be found in the same directory as this script # or in the cwd. @@ -14,7 +14,7 @@ def usage_and_die(rc): print() print("This script is designed to copy and register the Python debug") - print("binaries. It looks for pythonXX_d.dll, pythoncomxx_d.dll etc,") + print("binaries. It looks for pythonXX_d.dll, pythoncomXX_d.dll etc,") print("and installs them to work correctly with Python debug builds.") print() print("You will generally find this script in the. zip file that") @@ -23,7 +23,7 @@ def usage_and_die(rc): sys.exit(rc) -if win32api.__file__.find("_d") > 0: +if os.path.splitext(os.path.basename(win32api.__file__))[0].endswith("_d"): print("This scripts appears to be running a DEBUG version of Python.") print("Please run it using a normal release build (python.exe)") usage_and_die(1) diff --git a/win32/src/PyTime.cpp b/win32/src/PyTime.cpp index 5d751d87b1..de0c32cda4 100644 --- a/win32/src/PyTime.cpp +++ b/win32/src/PyTime.cpp @@ -412,7 +412,7 @@ PyObject *PyWin_NewTime(PyObject *timeOb) PyObject *ret = PyDateTimeAPI->DateTime_FromTimestamp((PyObject *)(&PyWinDateTimeType), args, NULL); if (ret == NULL) { // datetime throws an OSError on failure, but for compatibility with - // Python 2.x, we turn that into a ValueError. + // Python 2, we turn that into a ValueError. PyErr_Clear(); PyErr_SetString(PyExc_ValueError, "invalid timestamp"); } diff --git a/win32/src/PyUnicode.cpp b/win32/src/PyUnicode.cpp index 34a3005789..5bb7b084dc 100644 --- a/win32/src/PyUnicode.cpp +++ b/win32/src/PyUnicode.cpp @@ -13,8 +13,8 @@ BOOL PyWinObject_AsPfnAllocatedWCHAR(PyObject *stringObject, void *(*pfnAllocato { BOOL rc = TRUE; if (PyBytes_Check(stringObject)) { - // XXX - this was ported from the python 2 string api - which I thought - // included the trailing \0. But the 3.x `Bytes` API does not (right?), + // XXX - this was ported from the Python 2 string api - which I thought + // included the trailing \0. But the Python 3 `Bytes` API does not (right?), // so there's some trailing \0 confusion here. Py_ssize_t cch = PyBytes_Size(stringObject); const char *buf = PyBytes_AsString(stringObject); @@ -175,7 +175,7 @@ void PyWin_AutoFreeBstr::SetBstr(BSTR bstr) BOOL PyWinObject_AsBstr(PyObject *stringObject, BSTR *pResult, BOOL bNoneOK /*= FALSE*/, DWORD *pResultLen /*= NULL*/) { BOOL rc = TRUE; - // This used to support bytes as we moved to 3.x, but a BSTR has always been + // This used to support bytes as we moved to Python 3, but a BSTR has always been // unicode (ie, you'd never *try* and use bytes to create it), so there's no // sane b/w compat reason to support that any more. if (PyUnicode_Check(stringObject)) { diff --git a/win32/src/PyWinTypes.h b/win32/src/PyWinTypes.h index edfdb0b69a..6a0d3462a6 100644 --- a/win32/src/PyWinTypes.h +++ b/win32/src/PyWinTypes.h @@ -69,10 +69,10 @@ typedef Py_ssize_t Py_hash_t; #include "mmsystem.h" #ifdef BUILD_PYWINTYPES -/* We are building pywintypesxx.dll */ +/* We are building pywintypesXX.dll */ #define PYWINTYPES_EXPORT __declspec(dllexport) #else -/* This module uses pywintypesxx.dll */ +/* This module uses pywintypesXX.dll */ #define PYWINTYPES_EXPORT __declspec(dllimport) #if defined(_MSC_VER) #if defined(DEBUG) || defined(_DEBUG) @@ -116,7 +116,7 @@ PYWINTYPES_EXPORT PyObject *PyWin_SetBasicCOMError(HRESULT hr); // ************* // strings, which are a bit of a mess! // -// This has gone from 2.x ascii-only, to 2.x+3.x ascii-or-unicode, to 3.x unicode-only, +// This has gone from Python 2 ascii-only, to Py2+Py3k ascii-or-unicode, to Python 3 unicode-only, // - this baggage means some strange APIs which convert to and from "char *" in various ways. // // A sizes/lengths are reported as a `DWORD` rather than a `Py_ssize_t`, that's what the callers diff --git a/win32/src/PyWinTypesmodule.cpp b/win32/src/PyWinTypesmodule.cpp index 76d8f9f385..3dfaefc397 100644 --- a/win32/src/PyWinTypesmodule.cpp +++ b/win32/src/PyWinTypesmodule.cpp @@ -334,16 +334,6 @@ PyObject *PyWin_SetBasicCOMError(HRESULT hr) return NULL; } -// @pymethod string|pywintypes|Unicode|Creates a new Unicode object -PYWINTYPES_EXPORT PyObject *PyWin_NewUnicode(PyObject *self, PyObject *args) -{ - char *string; - int slen; - if (!PyArg_ParseTuple(args, "t#", &string, &slen)) - return NULL; - return PyUnicode_DecodeMBCS(string, slen, NULL); -} - // @pymethod string|pywintypes|UnicodeFromRaw|Creates a new Unicode object from raw binary data static PyObject *PyWin_NewUnicodeFromRaw(PyObject *self, PyObject *args) { @@ -465,7 +455,7 @@ BOOL PyWinObject_AsDWORDArray(PyObject *obdwords, DWORD **pdwords, DWORD *item_c for (tuple_index = 0; tuple_index < *item_cnt; tuple_index++) { tuple_item = PyTuple_GET_ITEM(dwords_tuple, tuple_index); // Doesn't check for overflow, but will accept a python long - // greater than INT_MAX (even on python 2.3). Also accepts + // greater than INT_MAX (even on Python 2.3). Also accepts // negatives and converts to the correct hex representation (*pdwords)[tuple_index] = PyLong_AsUnsignedLongMask(tuple_item); if (((*pdwords)[tuple_index] == -1) && PyErr_Occurred()) { @@ -760,7 +750,6 @@ static struct PyMethodDef pywintypes_functions[] = { {"DosDateTimeToTime", PyWin_DosDateTimeToTime, 1}, // @pymeth DosDateTimeToTime|Converts an MS-DOS Date/Time to a standard Time object #endif - {"Unicode", PyWin_NewUnicode, 1}, // @pymeth Unicode|Creates a new string object {"UnicodeFromRaw", PyWin_NewUnicodeFromRaw, 1}, // @pymeth UnicodeFromRaw|Creates a new string object from raw binary data {"IsTextUnicode", PyWin_IsTextUnicode, @@ -881,7 +870,7 @@ int PyWinGlobals_Ensure() // @tupleitem 3|None/int|argerror|The index of the argument in error, or (usually) None or -1 } - /* PyType_Ready *needs* to be called anytime pywintypesxx.dll is loaded, since + /* PyType_Ready *needs* to be called anytime pywintypesXX.dll is loaded, since other extension modules can use types defined here without pywintypes itself having been imported. ??? All extension modules that call this need to be changed to check the exit code ??? @@ -960,10 +949,6 @@ PYWIN_MODULE_INIT_FUNC(pywintypes) PYWIN_MODULE_INIT_RETURN_ERROR; ADD_CONSTANT(WAVE_FORMAT_PCM); - // Add a few types. - if (PyDict_SetItemString(dict, "UnicodeType", (PyObject *)&PyUnicode_Type) == -1) - PYWIN_MODULE_INIT_RETURN_ERROR; - if (!_PyWinDateTime_PrepareModuleDict(dict)) PYWIN_MODULE_INIT_RETURN_ERROR; #ifndef NO_PYWINTYPES_IID diff --git a/win32/src/_win32sysloader.cpp b/win32/src/_win32sysloader.cpp index c19a6dbb03..2a5db22ce4 100644 --- a/win32/src/_win32sysloader.cpp +++ b/win32/src/_win32sysloader.cpp @@ -9,7 +9,7 @@ See pywintypes.py for more information. ********************************************************************/ #include "windows.h" -// Windows rpc.h defines "small" as "char" and Python 3.x's accu.h uses +// Windows rpc.h defines "small" as "char" and Python 3's accu.h uses // "small" as a structure element causing compilation errors :( #ifdef small #undef small diff --git a/win32/src/odbc.cpp b/win32/src/odbc.cpp index 01fe518fc3..dfbe4c4fe1 100644 --- a/win32/src/odbc.cpp +++ b/win32/src/odbc.cpp @@ -1606,7 +1606,7 @@ PYWIN_MODULE_INIT_FUNC(odbc) } /* Names of various sql datatypes. - 's' format of Py_BuildValue creates unicode on py3k, and char string on 2.x + 's' format of Py_BuildValue creates unicode on py3k, and char string on Python 2 */ char *szDbiString = "STRING"; char *szDbiRaw = "RAW"; diff --git a/win32/src/win32apimodule.cpp b/win32/src/win32apimodule.cpp index f58e67e973..eee50d845e 100644 --- a/win32/src/win32apimodule.cpp +++ b/win32/src/win32apimodule.cpp @@ -5177,9 +5177,6 @@ PyObject *PyEnumResourceLanguages(PyObject *self, PyObject *args) return ret; } -// @pymethod |win32api|Unicode|Creates a new Unicode object -PYWINTYPES_EXPORT PyObject *PyWin_NewUnicode(PyObject *self, PyObject *args); - /////////////////// // // Win32 Exception Handler. @@ -6159,7 +6156,7 @@ static struct PyMethodDef win32api_functions[] = { // specified offset into the extra class memory for the window. {"SetClassWord", PySetClassWord, 1}, // @pymeth SetClassWord|Replaces the specified 32-bit (long) value at the // specified offset into the extra class memory for the window. - {"SetClassWord", PySetWindowWord, 1}, // @pymeth SetWindowWord| + {"SetWindowWord", PySetWindowWord, 1}, // @pymeth SetWindowWord| {"SetCursor", PySetCursor, 1}, // @pymeth SetCursor|Set the cursor to the HCURSOR object. // @pymeth SetEnvironmentVariable|Creates, deletes, or changes the value of an environment variable. {"SetEnvironmentVariable", PySetEnvironmentVariableW, 1}, @@ -6182,7 +6179,6 @@ static struct PyMethodDef win32api_functions[] = { {"TerminateProcess", PyTerminateProcess, 1}, // @pymeth TerminateProcess|Terminates a process. {"ToAsciiEx", PyToAsciiEx, 1}, // @pymeth ToAsciiEx|Translates the specified virtual-key code and keyboard state to // the corresponding character or characters. - {"Unicode", PyWin_NewUnicode, 1}, // @pymeth Unicode|Creates a new object {"UpdateResource", PyUpdateResource, 1}, // @pymeth UpdateResource|Updates a resource in a PE file. {"VkKeyScan", PyVkKeyScan, 1}, // @pymeth VkKeyScan|Translates a character to the corresponding virtual-key code and shift state. diff --git a/win32/src/win32clipboardmodule.cpp b/win32/src/win32clipboardmodule.cpp index 2dde670caa..8f552f6917 100644 --- a/win32/src/win32clipboardmodule.cpp +++ b/win32/src/win32clipboardmodule.cpp @@ -805,6 +805,11 @@ static PyObject *py_register_clipboard_format(PyObject *self, PyObject *args) // info. } +static bool isTextFormat(int format) +{ + return ((format == CF_TEXT) || (format == CF_UNICODETEXT) || (format == CF_OEMTEXT)); +} + //***************************************************************************** // // @pymethod int|win32clipboard|SetClipboardData|The SetClipboardData function @@ -845,15 +850,19 @@ static PyObject *py_set_clipboard_data(PyObject *self, PyObject *args) PyWinBufferView pybuf; // In py3k, unicode no longer supports buffer interface if (PyUnicode_Check(obhandle)) { - buf = tmpw = obhandle; if (!tmpw) return NULL; - bufSize = (tmpw.length + 1) * sizeof(WCHAR); + buf = tmpw = obhandle; + if (!tmpw) + return NULL; + bufSize = tmpw.length * sizeof(WCHAR); + if (isTextFormat(format)) + bufSize += sizeof(WCHAR); } else { if (!pybuf.init(obhandle)) return NULL; buf = pybuf.ptr(); bufSize = pybuf.len(); - if (PyBytes_Check(obhandle)) + if ((PyBytes_Check(obhandle)) && (isTextFormat(format))) bufSize++; // size doesnt include nulls! // else assume buffer needs no terminator... } diff --git a/win32/src/win32credmodule.cpp b/win32/src/win32credmodule.cpp index 7fd9f947fc..4856010e15 100644 --- a/win32/src/win32credmodule.cpp +++ b/win32/src/win32credmodule.cpp @@ -62,14 +62,14 @@ BOOL PyWinObject_AsCREDENTIAL_ATTRIBUTE(PyObject *obattr, PCREDENTIAL_ATTRIBUTE if (!PyWinObject_AsWCHAR(obKeyword, &attr->Keyword, FALSE)) { goto done; } - // Handle `Value`: the docs https://docs.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credential_attributew - // say it's an LPBYTE Value (meaning it's just bytes) but then the description says "Data - // associated with the attribute. By convention, if Value is a text string, then Value should - // not include the trailing zero character and should be in UNICODE." + // Handle `Value`: the docs + // https://docs.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credential_attributew say it's an LPBYTE + // Value (meaning it's just bytes) but then the description says "Data associated with the attribute. By convention, + // if Value is a text string, then Value should not include the trailing zero character and should be in UNICODE." if (PyUnicode_Check(obValue)) { Py_ssize_t nchars = PyUnicode_GetLength(obValue); Py_ssize_t nbytes = nchars * sizeof(wchar_t); - attr->ValueSize = nbytes; + attr->ValueSize = nbytes; if (attr->ValueSize == -1) { goto done; } @@ -82,7 +82,8 @@ BOOL PyWinObject_AsCREDENTIAL_ATTRIBUTE(PyObject *obattr, PCREDENTIAL_ATTRIBUTE if (PyUnicode_AsWideChar(obValue, (wchar_t *)attr->Value, nchars) == -1) { goto done; } - } else { + } + else { // Use the buffer API to get bytes if possible. if (!pybuf.init(obValue)) { goto done; @@ -472,7 +473,7 @@ PyObject *PyCredUnmarshalCredential(PyObject *self, PyObject *args, PyObject *kw case CertCredential: ret = Py_BuildValue("kN", credtype, PyBytes_FromStringAndSize((char *)&((PCERT_CREDENTIAL_INFO)credential)->rgbHashOfCert, - CERT_HASH_LENGTH)); + CERT_HASH_LENGTH)); break; // @flag UsernameTargetCredential|Unicode string containing username case UsernameTargetCredential: @@ -622,16 +623,53 @@ PyObject *PyCredReadDomainCredentials(PyObject *self, PyObject *args, PyObject * PyObject *PyCredDelete(PyObject *self, PyObject *args, PyObject *kwargs) { static char *keywords[] = {"TargetName", "Type", "Flags", NULL}; - PyObject *obtargetname, *ret = NULL; + PyObject *obtargetname, *ret = NULL, *obtarget; WCHAR *targetname; DWORD cred_type, flags = 0; - if (!PyArg_ParseTupleAndKeywords( args, kwargs, "Ok|k:CredDelete", keywords, &obtargetname, // @pyparm |TargetName||Target of credential to be deleted &cred_type, // @pyparm int|Type||One of the CRED_TYPE_* values &flags)) // @pyparm int|Flags|0|Reserved, use only 0 - return NULL; + { + char *kwds[] = {"Target", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:CredDelete", kwds, + &obtarget // @pyparm |Target||Credential to be deleted + )) + return NULL; + PyErr_Clear(); + if (!PyDict_Check(obtarget)) { + PyErr_SetString(PyExc_TypeError, + "First argument must be either a dictionary (no other arguments allowed) or a string " + "(other arguments required)"); + return NULL; + } + obtargetname = PyDict_GetItemString(obtarget, keywords[0]); + if (!obtargetname) { + PyErr_SetString(PyExc_KeyError, keywords[0]); + return NULL; + } + PyObject *val = PyDict_GetItemString(obtarget, keywords[1]); + if (!val) { + PyErr_SetString(PyExc_KeyError, keywords[1]); + return NULL; + } + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Argument should be int"); + return NULL; + } + cred_type = PyLong_AsUnsignedLong(val); + val = PyDict_GetItemString(obtarget, keywords[2]); + if (!val) { + PyErr_SetString(PyExc_KeyError, keywords[2]); + return NULL; + } + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Argument should be int"); + return NULL; + } + flags = PyLong_AsUnsignedLong(val); + } if (!PyWinObject_AsWCHAR(obtargetname, &targetname, FALSE)) return NULL; if (!CredDelete(targetname, cred_type, flags)) @@ -1169,5 +1207,7 @@ PYWIN_MODULE_INIT_FUNC(win32cred) PyModule_AddIntConstant(module, "CREDUI_MAX_USERNAME_LENGTH", CREDUI_MAX_USERNAME_LENGTH); PyModule_AddIntConstant(module, "CREDUI_MAX_PASSWORD_LENGTH", CREDUI_MAX_PASSWORD_LENGTH); + PyModule_AddIntConstant(module, "CRED_ENUMERATE_ALL_CREDENTIALS", CRED_ENUMERATE_ALL_CREDENTIALS); + PYWIN_MODULE_INIT_RETURN_SUCCESS; } diff --git a/win32/src/win32file.i b/win32/src/win32file.i index 3617964417..e2b9d0823f 100644 --- a/win32/src/win32file.i +++ b/win32/src/win32file.i @@ -709,7 +709,7 @@ static PyObject *PySetFileTime (PyObject *self, PyObject *args, PyObject *kwargs PyObject *obLastWriteTime = Py_None; // @pyparm |LastWriteTime|None|File written time. None for no change. BOOL UTCTimes = FALSE; // @pyparm boolean|UTCTimes|False|If True, input times are treated as UTC and no conversion is done, // otherwise they are treated as local times. Defaults to False for backward compatibility. - // This parameter is ignored in Python 3.x, where you should always pass datetime objects + // This parameter is ignored in Python 3, where you should always pass datetime objects // with timezone information. static char *keywords[] = {"File", "CreationTime", "LastAccessTime", "LastWriteTime", "UTCTimes", NULL}; diff --git a/win32/src/win32gui.i b/win32/src/win32gui.i index 3c429c940f..0bb66da960 100644 --- a/win32/src/win32gui.i +++ b/win32/src/win32gui.i @@ -7699,3 +7699,9 @@ BOOLAPI UnregisterDeviceNotification(HANDLE); // @pyparm int|Modifiers||Control keys, combination of win32con.MOD_* // @pyparm int|vk||Virtual key code BOOLAPI RegisterHotKey(HWND, int, UINT, UINT); + +// @pyswig |UnregisterHotKey|Unregisters a previously registeredhotkey +// @pyseeapi UnregisterHotKey +// @pyparm |hWnd||A handle to the window associated with the hot key to be freed +// @pyparm int|id||The identifier of the hot key +BOOLAPI UnregisterHotKey(HWND, int); diff --git a/win32/src/win32helpmodule.cpp b/win32/src/win32helpmodule.cpp index 31e8d6d4f9..f9094b5ed1 100644 --- a/win32/src/win32helpmodule.cpp +++ b/win32/src/win32helpmodule.cpp @@ -21,7 +21,7 @@ generates Windows .hlp files. #if _MSC_VER == 1500 // This uses htmlhelp.lib, which causes an unresolved external for -// __report_rangecheckfailure with vs2008 (which is what we used for python 2.x) +// __report_rangecheckfailure with vs2008 (which is what we used for Python 2) // No idea why, but we define it here and cause it to kill the process if it is ever hit. extern "C" __declspec(noreturn, dllexport) void __cdecl __report_rangecheckfailure(void) { ::ExitProcess(1); } #endif diff --git a/win32/test/test_exceptions.py b/win32/test/test_exceptions.py index ad956abf66..984d7271ba 100644 --- a/win32/test/test_exceptions.py +++ b/win32/test/test_exceptions.py @@ -1,4 +1,5 @@ """Test pywin32's error semantics""" + import unittest import pythoncom diff --git a/win32/test/test_win32clipboard.py b/win32/test/test_win32clipboard.py new file mode 100644 index 0000000000..f5d5c28d14 --- /dev/null +++ b/win32/test/test_win32clipboard.py @@ -0,0 +1,60 @@ +# tests for win32gui +import unittest + +import win32clipboard + + +class TestGetSetClipboardData(unittest.TestCase): + + def copyData(self, data, format_): + win32clipboard.OpenClipboard() + ret = None + try: + win32clipboard.SetClipboardData(format_, data) + ret = win32clipboard.GetClipboardData(format_) + finally: + win32clipboard.CloseClipboard() + return ret + + def copyText(self, data, format_): + win32clipboard.OpenClipboard() + ret = None + try: + win32clipboard.SetClipboardText(data, format_) + ret = win32clipboard.GetClipboardData(format_) + finally: + win32clipboard.CloseClipboard() + return ret + + def test_data(self): + test_data = { + "Dummy str": win32clipboard.CF_UNICODETEXT, + b"Dummy bytes text": win32clipboard.CF_TEXT, + b"Dummy\x00\xFF bytes": win32clipboard.CF_DIB, + } + for data, fmt in test_data.items(): + self.assertEqual(data, self.copyData(data, fmt)) + test_data = { + "Dummy str": (win32clipboard.CF_TEXT, win32clipboard.CF_DIB), + b"Dummy\x00\xFF bytes": (win32clipboard.CF_UNICODETEXT,), + } + for data, formats in test_data.items(): + for fmt in formats: + self.assertNotEqual(data, self.copyData(data, fmt)) + + def test_text(self): + test_data = { + "Dummy str": win32clipboard.CF_UNICODETEXT, + b"Dummy bytes": win32clipboard.CF_TEXT, + } + for data, fmt in test_data.items(): + self.assertEqual(data, self.copyText(data, fmt)) + self.assertRaises(ValueError, self.copyText, data, win32clipboard.CF_DIB) + s = "Dummy str" + self.assertEqual( + s.encode(), self.copyText(s, win32clipboard.CF_TEXT) + ) # @TODO - cfati: Do we want this? + + +if __name__ == "__main__": + unittest.main() diff --git a/win32/test/test_win32cred.py b/win32/test/test_win32cred.py new file mode 100644 index 0000000000..e4af181353 --- /dev/null +++ b/win32/test/test_win32cred.py @@ -0,0 +1,73 @@ +import copy +import unittest + +import win32cred + + +class TestCredFunctions(unittest.TestCase): + + def setUp(self): + self.flags = 0 + self.dummy_cred = { + "TargetName": "DumyyUser", + "Type": win32cred.CRED_TYPE_GENERIC, + "Flags": self.flags, + } + + def create_dummy_cred(self): + cred = copy.deepcopy(self.dummy_cred) + cred.update( + { + "Persist": win32cred.CRED_PERSIST_SESSION, + } + ) + try: + win32cred.CredWrite(cred, self.flags) + except Exception as e: + print(e) + + def is_dummy_cred(self): + return ( + len( + [ + e + for e in win32cred.CredEnumerate() + if e["TargetName"] == self.dummy_cred["TargetName"] + ] + ) + == 1 + ) + + def test_creddelete(self): + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(**self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(*self.dummy_cred.values())) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone( + win32cred.CredDelete(self.dummy_cred["TargetName"], self.dummy_cred["Type"]) + ) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertIsNone(win32cred.CredDelete(Target=self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) + self.create_dummy_cred() + self.assertTrue(self.is_dummy_cred()) + self.assertRaises(TypeError, win32cred.CredDelete, "") + self.assertRaises(KeyError, win32cred.CredDelete, {}) + self.assertRaises(KeyError, win32cred.CredDelete, {"TargetName": ""}) + self.assertRaises( + TypeError, win32cred.CredDelete, {"TargetName": "", "Type": 3.141593} + ) + self.assertIsNone(win32cred.CredDelete(self.dummy_cred)) + self.assertFalse(self.is_dummy_cred()) diff --git a/win32/test/test_win32crypt.py b/win32/test/test_win32crypt.py index 4d1fa712e4..145a3df96f 100644 --- a/win32/test/test_win32crypt.py +++ b/win32/test/test_win32crypt.py @@ -56,9 +56,11 @@ def open_windows_certstore(store_name: str, store_location: str) -> Iterator[Any CERT_STORE_PROV_SYSTEM, 0, None, - CERT_SYSTEM_STORE_LOCAL_MACHINE - if store_location == _LOCAL_MACHINE - else CERT_SYSTEM_STORE_CURRENT_USER, + ( + CERT_SYSTEM_STORE_LOCAL_MACHINE + if store_location == _LOCAL_MACHINE + else CERT_SYSTEM_STORE_CURRENT_USER + ), store_name, ) yield handle diff --git a/win32/test/test_win32profile.py b/win32/test/test_win32profile.py index 84dc83bc9d..71432363fd 100644 --- a/win32/test/test_win32profile.py +++ b/win32/test/test_win32profile.py @@ -1,4 +1,5 @@ """Test win32profile""" + import os import unittest diff --git a/win32/test/testall.py b/win32/test/testall.py index 72aa42fc84..334dc0d91d 100644 --- a/win32/test/testall.py +++ b/win32/test/testall.py @@ -142,7 +142,7 @@ def import_all(): dir = os.path.dirname(win32api.__file__) num = 0 - is_debug = os.path.basename(win32api.__file__).endswith("_d") + is_debug = os.path.splitext(os.path.basename(win32api.__file__))[0].endswith("_d") for name in os.listdir(dir): base, ext = os.path.splitext(name) # handle `modname.cp310-win_amd64.pyd` etc @@ -150,12 +150,7 @@ def import_all(): if ( (ext == ".pyd") and name != "_winxptheme.pyd" - and ( - is_debug - and base.endswith("_d") - or not is_debug - and not base.endswith("_d") - ) + and is_debug == base.endswith("_d") ): try: __import__(base)