From b922bf5bab4d098000e4a604ac46a60ae685b3f4 Mon Sep 17 00:00:00 2001 From: Thomas Geppert Date: Fri, 18 Oct 2024 14:52:55 +0200 Subject: [PATCH 1/4] Implement the creation of SAFEARRAY(VT_RECORD) from a sequence of COM Records (#2317) * Implemented the creation of SAFEARRAY(VT_RECORD) from a sequence of COM Records and the possibility to use instances of this type as COM Record fields. * Added a untitest for COM Records with SAFEARRAY(VT_RECORD) fields. * Added CHANGELOG entry. --- CHANGES.txt | 3 ++- com/TestSources/PyCOMTest/PyCOMImpl.cpp | 21 ++++++++++++++++ com/TestSources/PyCOMTest/PyCOMImpl.h | 1 + com/TestSources/PyCOMTest/PyCOMTest.idl | 6 +++++ com/win32com/src/PyRecord.cpp | 11 ++++++--- com/win32com/src/oleargs.cpp | 33 ++++++++++++++++++++++--- com/win32com/test/testPyComTest.py | 14 +++++++++++ 7 files changed, 80 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d8978afd5..82cf59213 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,7 +15,8 @@ Coming in build 309, as yet unreleased -------------------------------------- * Dropped support for Python 3.7 (#2207, @Avasam) -* Implement record pointers as [in, out] method parameters of a Dispatch Interface (#2310) +* Implement the creation of SAFEARRAY(VT_RECORD) from a sequence of COM Records (#2317, @geppi) +* Implement record pointers as [in, out] method parameters of a Dispatch Interface (#2304, #2310, @geppi) * Fix memory leak converting to PyObject from some SAFEARRAY elements (#2316) * Fix bug where makepy support was unnecessarily generated (#2354, #2353, @geppi) * Fail sooner on invalid `win32timezone.TimeZoneInfo` creation (#2338, @Avasam) diff --git a/com/TestSources/PyCOMTest/PyCOMImpl.cpp b/com/TestSources/PyCOMTest/PyCOMImpl.cpp index d58c00f99..6ba0af0c4 100644 --- a/com/TestSources/PyCOMTest/PyCOMImpl.cpp +++ b/com/TestSources/PyCOMTest/PyCOMImpl.cpp @@ -626,6 +626,27 @@ HRESULT CPyCOMTest::ModifyStruct(TestStruct1 *prec) return S_OK; } +HRESULT CPyCOMTest::VerifyArrayOfStructs(TestStruct2 *prec, VARIANT_BOOL *is_ok) +{ + long i; + TestStruct1 *pdata = NULL; + HRESULT hr; + + hr = SafeArrayAccessData(prec->array_of_records, reinterpret_cast(&pdata)); + if (FAILED(hr)) { + return E_FAIL; + } + *is_ok = VARIANT_TRUE; + for (i = 0; i < prec->rec_count; i++) + { + if (_wcsicmp(pdata[i].str_value, L"This is record number") != 0 || pdata[i].int_value != i + 1) { + *is_ok = VARIANT_FALSE; + break; + } + } + return S_OK; +} + HRESULT CPyCOMTest::DoubleString(BSTR in, BSTR *out) { *out = SysAllocStringLen(NULL, SysStringLen(in) * 2); diff --git a/com/TestSources/PyCOMTest/PyCOMImpl.h b/com/TestSources/PyCOMTest/PyCOMImpl.h index 50eb8dd0d..7b1f83c2b 100644 --- a/com/TestSources/PyCOMTest/PyCOMImpl.h +++ b/com/TestSources/PyCOMTest/PyCOMImpl.h @@ -120,6 +120,7 @@ class CPyCOMTest : public IDispatchImplowner); else if (V_VT(&vret) == (VT_BYREF | VT_ARRAY | VT_RECORD)) { SAFEARRAY *psa = *V_ARRAYREF(&vret); - int d = SafeArrayGetDim(psa); - if (sub_data == NULL) - return PyErr_Format(PyExc_RuntimeError, "Did not get a buffer for the array!"); if (SafeArrayGetDim(psa) != 1) return PyErr_Format(PyExc_TypeError, "Only support single dimensional arrays of records"); IRecordInfo *sub = NULL; @@ -526,7 +523,13 @@ PyObject *PyRecord::getattro(PyObject *self, PyObject *obname) ret_tuple = PyTuple_New(nelems); if (ret_tuple == NULL) goto array_end; - this_data = (BYTE *)sub_data; + // We're dealing here with a Record field that is a SAFEARRAY of Records. + // Therefore the VARIANT that was returned by the call to 'pyrec->pri->GetFieldNoCopy' + // does contain a reference to the SAFEARRAY of Records, i.e. the actual data of the + // Record elements of this SAFEARRAY is referenced by the 'pvData' field of the SAFEARRAY. + // In this particular case the implementation of 'GetFieldNoCopy' returns a NULL pointer + // in the last parameter, i.e. 'sub_data == NULL'. + this_data = (BYTE *)psa->pvData; for (i = 0; i < nelems; i++) { PyTuple_SET_ITEM(ret_tuple, i, new PyRecord(sub, this_data, pyrec->owner)); this_data += element_size; diff --git a/com/win32com/src/oleargs.cpp b/com/win32com/src/oleargs.cpp index a1338bab5..91fded5b1 100644 --- a/com/win32com/src/oleargs.cpp +++ b/com/win32com/src/oleargs.cpp @@ -4,6 +4,7 @@ #include "stdafx.h" #include "PythonCOM.h" +#include "PyRecord.h" extern PyObject *PyObject_FromRecordInfo(IRecordInfo *, void *, ULONG); extern PyObject *PyObject_FromSAFEARRAYRecordInfo(SAFEARRAY *psa); @@ -278,9 +279,23 @@ BOOL PyCom_VariantFromPyObject(PyObject *obj, VARIANT *var) // So make sure this check is after anything else which qualifies. else if (PySequence_Check(obj)) { V_ARRAY(var) = NULL; // not a valid, existing array. - if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var))) - return FALSE; - V_VT(var) = VT_ARRAY | VT_VARIANT; + BOOL is_record_item = false; + if (PyObject_Length(obj) > 0) { + PyObject *obItemCheck = PySequence_GetItem(obj, 0); + is_record_item = PyRecord_Check(obItemCheck); + } + // If the sequence elements are PyRecord objects we do NOT package + // them as VARIANT elements but put them directly into the SAFEARRAY. + if (is_record_item) { + if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var), VT_RECORD)) + return FALSE; + V_VT(var) = VT_ARRAY | VT_RECORD; + } + else { + if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var))) + return FALSE; + V_VT(var) = VT_ARRAY | VT_VARIANT; + } } else if (PyRecord_Check(obj)) { if (!PyObject_AsVARIANTRecordInfo(obj, var)) @@ -554,6 +569,9 @@ static BOOL PyCom_SAFEARRAYFromPyObjectBuildDimension(PyObject *obj, SAFEARRAY * helper.m_reqdType = vt; ok = helper.MakeObjToVariant(item, &element); switch (vt) { + case VT_RECORD: + pvData = V_RECORD(&element); + break; case VT_DISPATCH: pvData = V_DISPATCH(&element); break; @@ -759,7 +777,14 @@ static BOOL PyCom_SAFEARRAYFromPyObjectEx(PyObject *obj, SAFEARRAY **ppSA, bool if (bAllocNewArray) { // OK - Finally can create the array... - *ppSA = SafeArrayCreate(vt, cDims, pBounds); + if (vt == VT_RECORD) { + // SAFEARRAYS of UDTs need a special treatment. + obItemCheck = PySequence_GetItem(obj, 0); + PyRecord *pyrec = (PyRecord *)obItemCheck; + *ppSA = SafeArrayCreateEx(vt, cDims, pBounds, pyrec->pri); + } + else + *ppSA = SafeArrayCreate(vt, cDims, pBounds); if (*ppSA == NULL) { delete[] pBounds; PyErr_SetString(PyExc_MemoryError, "CreatingSafeArray"); diff --git a/com/win32com/test/testPyComTest.py b/com/win32com/test/testPyComTest.py index dbe933669..50330e356 100644 --- a/com/win32com/test/testPyComTest.py +++ b/com/win32com/test/testPyComTest.py @@ -411,6 +411,7 @@ def TestDynamic(): def TestGenerated(): # Create an instance of the server. + from win32com.client import Record from win32com.client.gencache import EnsureDispatch o = EnsureDispatch("PyCOMTest.PyCOMTest") @@ -433,6 +434,19 @@ def TestGenerated(): coclass = GetClass("{B88DD310-BAE8-11D0-AE86-76F2C1000000}")() TestCounter(coclass, True) + # Test records with SAFEARRAY(VT_RECORD) fields. + progress("Testing records with SAFEARRAY(VT_RECORD) fields.") + l = [] + for i in range(3): + rec = Record("TestStruct1", o) + rec.str_value = "This is record number" + rec.int_value = i + 1 + l.append(rec) + test_rec = Record("TestStruct2", o) + test_rec.array_of_records = l + test_rec.rec_count = i + 1 + assert o.VerifyArrayOfStructs(test_rec) + # XXX - this is failing in dynamic tests, but should work fine. i1, i2 = o.GetMultipleInterfaces() # Yay - is now an instance returned! From b9256ff26bb64a85a11defe9c817bdde08ab0560 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 10 Oct 2024 21:17:21 -0400 Subject: [PATCH 2/4] Update configs to use Ruff format instead of Black --- .editorconfig | 2 +- .github/workflows/main.yml | 13 +- .pre-commit-config.yaml | 6 +- adodbapi/adodbapi.py | 19 +- adodbapi/process_connect_string.py | 272 ++++++++++++++--------------- com/win32comext/adsi/demos/scp.py | 2 +- ruff.toml | 3 +- win32/Lib/win32gui_struct.py | 6 +- win32/Lib/win32pdhquery.py | 5 +- win32/test/test_win32file.py | 11 +- 10 files changed, 167 insertions(+), 172 deletions(-) diff --git a/.editorconfig b/.editorconfig index 51baf1204..6874683d6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ indent_size = 4 max_line_length = 120 # Same as .clang-format [*.py] -max_line_length = 88 # Same as Black +max_line_length = 88 # Same as Ruff's default [*.md] trim_trailing_whitespace = false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2af0d70a0..db40b4254 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -110,23 +110,22 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - # This job only needs to target the oldest version supported by our checkers - # (black>=24.10.0 supports Python >=3.9) - python-version: "3.9" + # This job only needs to target the oldest supported version + python-version: "3.8" cache: pip cache-dependency-path: .github/workflows/main.yml - run: pip install clang-format pycln - run: pycln . --config=pycln.toml --check - uses: astral-sh/ruff-action@v1 with: - version: "0.4.5" - - uses: psf/black@stable + version: "0.4.9" + - uses: astral-sh/ruff-action@v1 with: - options: "--fast --check --diff --verbose" + version: "0.4.9" + args: "format --check" - run: | # Too many files to fit in a single command, also exclude vendored Scintilla and MAPIStubLibrary clang-format --Werror --dry-run $(git ls-files '*.cpp') clang-format --Werror --dry-run $(git ls-files '*.h' ':!:Pythonwin/Scintilla/' ':!:com/win32comext/mapi/src/MAPIStubLibrary/') - shell: powershell mypy: runs-on: windows-2019 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a89a958b7..566983fae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,11 +29,7 @@ repos: hooks: - id: ruff # Run the linter. args: [--fix] - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 - hooks: - - id: black - verbose: true + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-clang-format rev: v18.1.5 hooks: diff --git a/adodbapi/adodbapi.py b/adodbapi/adodbapi.py index 4e85bc525..b63b829d8 100644 --- a/adodbapi/adodbapi.py +++ b/adodbapi/adodbapi.py @@ -395,9 +395,7 @@ def _rollback(self): # If attributes has adXactAbortRetaining it performs retaining aborts that is, # calling RollbackTrans automatically starts a new transaction. Not all providers support this. # If not, we will have to start a new transaction by this command: - if ( - not self.transaction_level - ): # if self.transaction_level == 0 or self.transaction_level is None: + if not self.transaction_level: self.transaction_level = self.connector.BeginTrans() except Exception as e: self._raiseConnectionError(api.ProgrammingError, e) @@ -634,9 +632,8 @@ def _makeDescriptionFromRS(self): if self.rs.EOF or self.rs.BOF: display_size = None else: - display_size = ( - f.ActualSize - ) # TODO: Is this the correct defintion according to the DB API 2 Spec ? + # TODO: Is this the correct defintion according to the DB API 2 Spec ? + display_size = f.ActualSize null_ok = bool(f.Attributes & adc.adFldMayBeNull) # v2.1 Cole desc.append( ( @@ -774,9 +771,8 @@ def get_returned_parameters(self): after the last recordset has been read. In that case, you must coll nextset() until it returns None, then call this method to get your returned information.""" - retLst = ( - [] - ) # store procedures may return altered parameters, including an added "return value" item + # store procedures may return altered parameters, including an added "return value" item + retLst = [] for p in tuple(self.cmd.Parameters): if verbose > 2: print( @@ -907,9 +903,8 @@ def _buildADOparameterList(self, parameters, sproc=False): ) i += 1 else: # -- build own parameter list - if ( - self._parameter_names - ): # we expect a dictionary of parameters, this is the list of expected names + # we expect a dictionary of parameters, this is the list of expected names + if self._parameter_names: for parm_name in self._parameter_names: elem = parameters[parm_name] adotype = api.pyTypeToADOType(elem) diff --git a/adodbapi/process_connect_string.py b/adodbapi/process_connect_string.py index d8b29f280..a134ad656 100644 --- a/adodbapi/process_connect_string.py +++ b/adodbapi/process_connect_string.py @@ -1,137 +1,135 @@ -""" a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" - -from . import is64bit - - -def macro_call(macro_name, args, kwargs): - """allow the programmer to perform limited processing on the server by passing macro names and args - - :new_key - the key name the macro will create - :args[0] - macro name - :args[1:] - any arguments - :code - the value of the keyword item - :kwargs - the connection keyword dictionary. ??key has been removed - --> the value to put in for kwargs['name'] = value - """ - if isinstance(args, (str, str)): - args = [ - args - ] # the user forgot to pass a sequence, so make a string into args[0] - new_key = args[0] - try: - if macro_name == "is64bit": - if is64bit.Python(): # if on 64 bit Python - return new_key, args[1] # return first argument - else: - try: - return new_key, args[2] # else return second argument (if defined) - except IndexError: - return new_key, "" # else return blank - - elif ( - macro_name == "getuser" - ): # get the name of the user the server is logged in under - if not new_key in kwargs: - import getpass - - return new_key, getpass.getuser() - - elif macro_name == "getnode": # get the name of the computer running the server - import platform - - try: - return new_key, args[1] % platform.node() - except IndexError: - return new_key, platform.node() - - elif macro_name == "getenv": # expand the server's environment variable args[1] - try: - dflt = args[2] # if not found, default from args[2] - except IndexError: # or blank - dflt = "" - return new_key, os.environ.get(args[1], dflt) - - elif macro_name == "auto_security": - if ( - not "user" in kwargs or not kwargs["user"] - ): # missing, blank, or Null username - return new_key, "Integrated Security=SSPI" - return new_key, "User ID=%(user)s; Password=%(password)s" % kwargs - - elif ( - macro_name == "find_temp_test_path" - ): # helper function for testing ado operation -- undocumented - import os - import tempfile - - return new_key, os.path.join( - tempfile.gettempdir(), "adodbapi_test", args[1] - ) - - raise ValueError(f"Unknown connect string macro={macro_name}") - except: - raise ValueError(f"Error in macro processing {macro_name} {args!r}") - - -def process( - args, kwargs, expand_macros=False -): # --> connection string with keyword arguments processed. - """attempts to inject arguments into a connection string using Python "%" operator for strings - - co: adodbapi connection object - args: positional parameters from the .connect() call - kvargs: keyword arguments from the .connect() call - """ - try: - dsn = args[0] - except IndexError: - dsn = None - if isinstance( - dsn, dict - ): # as a convenience the first argument may be django settings - kwargs.update(dsn) - elif ( - dsn - ): # the connection string is passed to the connection as part of the keyword dictionary - kwargs["connection_string"] = dsn - try: - a1 = args[1] - except IndexError: - a1 = None - # historically, the second positional argument might be a timeout value - if isinstance(a1, int): - kwargs["timeout"] = a1 - # if the second positional argument is a string, then it is user - elif isinstance(a1, str): - kwargs["user"] = a1 - # if the second positional argument is a dictionary, use it as keyword arguments, too - elif isinstance(a1, dict): - kwargs.update(a1) - try: - kwargs["password"] = args[2] # the third positional argument is password - kwargs["host"] = args[3] # the fourth positional argument is host name - kwargs["database"] = args[4] # the fifth positional argument is database name - except IndexError: - pass - - # make sure connection string is defined somehow - if not "connection_string" in kwargs: - try: # perhaps 'dsn' was defined - kwargs["connection_string"] = kwargs["dsn"] - except KeyError: - try: # as a last effort, use the "host" keyword - kwargs["connection_string"] = kwargs["host"] - except KeyError: - raise TypeError("Must define 'connection_string' for ado connections") - if expand_macros: - for kwarg in list(kwargs.keys()): - if kwarg.startswith("macro_"): # If a key defines a macro - macro_name = kwarg[6:] # name without the "macro_" - macro_code = kwargs.pop( - kwarg - ) # we remove the macro_key and get the code to execute - new_key, rslt = macro_call( - macro_name, macro_code, kwargs - ) # run the code in the local context - kwargs[new_key] = rslt # put the result back in the keywords dict - return kwargs +""" a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" + +from . import is64bit + + +def macro_call(macro_name, args, kwargs): + """allow the programmer to perform limited processing on the server by passing macro names and args + + :new_key - the key name the macro will create + :args[0] - macro name + :args[1:] - any arguments + :code - the value of the keyword item + :kwargs - the connection keyword dictionary. ??key has been removed + --> the value to put in for kwargs['name'] = value + """ + if isinstance(args, (str, str)): + args = [ + args + ] # the user forgot to pass a sequence, so make a string into args[0] + new_key = args[0] + try: + if macro_name == "is64bit": + if is64bit.Python(): # if on 64 bit Python + return new_key, args[1] # return first argument + else: + try: + return new_key, args[2] # else return second argument (if defined) + except IndexError: + return new_key, "" # else return blank + + elif ( + macro_name == "getuser" + ): # get the name of the user the server is logged in under + if not new_key in kwargs: + import getpass + + return new_key, getpass.getuser() + + elif macro_name == "getnode": # get the name of the computer running the server + import platform + + try: + return new_key, args[1] % platform.node() + except IndexError: + return new_key, platform.node() + + elif macro_name == "getenv": # expand the server's environment variable args[1] + try: + dflt = args[2] # if not found, default from args[2] + except IndexError: # or blank + dflt = "" + return new_key, os.environ.get(args[1], dflt) + + elif macro_name == "auto_security": + if ( + not "user" in kwargs or not kwargs["user"] + ): # missing, blank, or Null username + return new_key, "Integrated Security=SSPI" + return new_key, "User ID=%(user)s; Password=%(password)s" % kwargs + + elif ( + macro_name == "find_temp_test_path" + ): # helper function for testing ado operation -- undocumented + import os + import tempfile + + return new_key, os.path.join( + tempfile.gettempdir(), "adodbapi_test", args[1] + ) + + raise ValueError(f"Unknown connect string macro={macro_name}") + except: + raise ValueError(f"Error in macro processing {macro_name} {args!r}") + + +def process( + args, kwargs, expand_macros=False +): # --> connection string with keyword arguments processed. + """attempts to inject arguments into a connection string using Python "%" operator for strings + + co: adodbapi connection object + args: positional parameters from the .connect() call + kvargs: keyword arguments from the .connect() call + """ + try: + dsn = args[0] + except IndexError: + dsn = None + # as a convenience the first argument may be django settings + if isinstance(dsn, dict): + kwargs.update(dsn) + # the connection string is passed to the connection as part of the keyword dictionary + elif dsn: + kwargs["connection_string"] = dsn + try: + a1 = args[1] + except IndexError: + a1 = None + # historically, the second positional argument might be a timeout value + if isinstance(a1, int): + kwargs["timeout"] = a1 + # if the second positional argument is a string, then it is user + elif isinstance(a1, str): + kwargs["user"] = a1 + # if the second positional argument is a dictionary, use it as keyword arguments, too + elif isinstance(a1, dict): + kwargs.update(a1) + try: + kwargs["password"] = args[2] # the third positional argument is password + kwargs["host"] = args[3] # the fourth positional argument is host name + kwargs["database"] = args[4] # the fifth positional argument is database name + except IndexError: + pass + + # make sure connection string is defined somehow + if not "connection_string" in kwargs: + try: # perhaps 'dsn' was defined + kwargs["connection_string"] = kwargs["dsn"] + except KeyError: + try: # as a last effort, use the "host" keyword + kwargs["connection_string"] = kwargs["host"] + except KeyError: + raise TypeError("Must define 'connection_string' for ado connections") + if expand_macros: + for kwarg in list(kwargs.keys()): + if kwarg.startswith("macro_"): # If a key defines a macro + macro_name = kwarg[6:] # name without the "macro_" + macro_code = kwargs.pop( + kwarg + ) # we remove the macro_key and get the code to execute + new_key, rslt = macro_call( + macro_name, macro_code, kwargs + ) # run the code in the local context + kwargs[new_key] = rslt # put the result back in the keywords dict + return kwargs diff --git a/com/win32comext/adsi/demos/scp.py b/com/win32comext/adsi/demos/scp.py index b2ff016ec..dc5f831e8 100644 --- a/com/win32comext/adsi/demos/scp.py +++ b/com/win32comext/adsi/demos/scp.py @@ -419,7 +419,7 @@ def main(): "--test", action="store_true", help="Execute a mini-test suite, providing defaults for most options and args", - ), + ) parser.add_option( "", diff --git a/ruff.toml b/ruff.toml index eab7fcce1..6d75257db 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,4 +1,6 @@ target-version = "py38" # Target the oldest supported version in editors and default CLI +# This file is not UTF-8 +extend-exclude = ["Pythonwin/pywin/test/_dbgscript.py"] [lint] select = [ @@ -15,7 +17,6 @@ select = [ "FLY", # static-join-to-f-string "G", # flake8-logging-format # Note, we still want to allow multiline ISC - "ISC001", # single-line-implicit-string-concatenation "UP025", # Remove unicode literals from strings "UP030", # Use implicit references for positional format fields # TODO: Still lots of manual fixes needed diff --git a/win32/Lib/win32gui_struct.py b/win32/Lib/win32gui_struct.py index 41ef815fd..a4b7a4f4c 100644 --- a/win32/Lib/win32gui_struct.py +++ b/win32/Lib/win32gui_struct.py @@ -463,7 +463,7 @@ def EmptyTVITEM(hitem, mask=None, text_buf_size=512): else: text_addr = text_buf_size = 0 buf = struct.pack( - _tvitem_fmt, mask, hitem, 0, 0, text_addr, text_buf_size, 0, 0, 0, 0 # text + _tvitem_fmt, mask, hitem, 0, 0, text_addr, text_buf_size, 0, 0, 0, 0 ) return array.array("b", buf), extra @@ -758,7 +758,7 @@ def PackLVCOLUMN(fmt=None, cx=None, text=None, subItem=None, image=None, order=N text_addr, _ = text_buffer.buffer_info() text_len = len(text) buf = struct.pack( - _lvcolumn_fmt, mask, fmt, cx, text_addr, text_len, subItem, image, order # text + _lvcolumn_fmt, mask, fmt, cx, text_addr, text_len, subItem, image, order ) return array.array("b", buf), extra @@ -809,7 +809,7 @@ def EmptyLVCOLUMN(mask=None, text_buf_size=512): else: text_addr = text_buf_size = 0 buf = struct.pack( - _lvcolumn_fmt, mask, 0, 0, text_addr, text_buf_size, 0, 0, 0 # text + _lvcolumn_fmt, mask, 0, 0, text_addr, text_buf_size, 0, 0, 0 ) return array.array("b", buf), extra diff --git a/win32/Lib/win32pdhquery.py b/win32/Lib/win32pdhquery.py index 3f458e0a8..3e51b77b9 100644 --- a/win32/Lib/win32pdhquery.py +++ b/win32/Lib/win32pdhquery.py @@ -331,9 +331,8 @@ def collectdataslave(self, format=win32pdh.PDH_FMT_LONG): if not ok: temp.append(-1) # a better way to signal failure??? return temp - except ( - win32api.error - ): # will happen if, for instance, no counters are part of the query and we attempt to collect data for it. + # will happen if, for instance, no counters are part of the query and we attempt to collect data for it. + except win32api.error: return [-1] * len(self.counters) # pickle functions diff --git a/win32/test/test_win32file.py b/win32/test/test_win32file.py index 4763f9289..12ba8c31d 100644 --- a/win32/test/test_win32file.py +++ b/win32/test/test_win32file.py @@ -641,7 +641,10 @@ def _watcherThread(self, dn, dh, changes): try: print("waiting", dh) changes = win32file.ReadDirectoryChangesW( - dh, 8192, False, flags # sub-tree + dh, + 8192, + False, # sub-tree + flags, ) print("got", changes) except: @@ -655,7 +658,11 @@ def _watcherThreadOverlapped(self, dn, dh, changes): overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None) while 1: win32file.ReadDirectoryChangesW( - dh, buf, False, flags, overlapped # sub-tree + dh, + buf, + False, # sub-tree + flags, + overlapped, ) # Wait for our event, or for 5 seconds. rc = win32event.WaitForSingleObject(overlapped.hEvent, 5000) From b74bfdca97238735adbd1b20d7245cca7070900f Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 14 Oct 2024 13:18:52 -0400 Subject: [PATCH 3/4] Run ruff format --- AutoDuck/Dump2HHC.py | 28 ++--- Pythonwin/pywin/dialogs/list.py | 19 ++-- Pythonwin/pywin/framework/app.py | 3 +- Pythonwin/pywin/framework/scriptutils.py | 4 +- Pythonwin/pywin/framework/stdin.py | 1 + Pythonwin/pywin/mfc/activex.py | 3 +- adodbapi/examples/db_print.py | 2 +- adodbapi/examples/db_table_names.py | 2 +- adodbapi/process_connect_string.py | 2 +- adodbapi/test/adodbapitest.py | 33 +++--- adodbapi/test/dbapi20.py | 14 +-- adodbapi/test/setuptestframework.py | 1 + com/win32com/client/combrowse.py | 27 +++-- com/win32com/client/dynamic.py | 7 +- com/win32com/client/genpy.py | 4 +- com/win32com/client/makepy.py | 7 +- com/win32com/client/selecttlb.py | 3 +- com/win32com/makegw/makegw.py | 102 ++++++++---------- com/win32com/makegw/makegwenum.py | 8 +- com/win32com/makegw/makegwparse.py | 14 +-- com/win32com/olectl.py | 2 +- com/win32com/server/connect.py | 2 +- com/win32com/server/exception.py | 14 +-- com/win32com/servers/interp.py | 14 +-- com/win32com/test/errorSemantics.py | 6 +- com/win32com/test/testExchange.py | 1 - com/win32com/test/testvb.py | 15 +-- com/win32com/util.py | 2 +- com/win32comext/axdebug/adb.py | 3 +- com/win32comext/axdebug/contexts.py | 4 +- com/win32comext/axdebug/documents.py | 3 +- com/win32comext/axdebug/dump.py | 4 +- com/win32comext/axscript/client/error.py | 4 +- com/win32comext/axscript/client/framework.py | 8 +- .../axscript/client/scriptdispatch.py | 6 +- isapi/samples/redirector_with_filter.py | 1 + isapi/simple.py | 1 + isapi/threaded_extension.py | 1 + setup.py | 68 +++--------- win32/Demos/security/GetTokenInformation.py | 4 +- win32/Demos/win32ts_logoff_disconnected.py | 2 +- win32/Lib/win32evtlogutil.py | 3 +- win32/Lib/win32gui_struct.py | 4 +- win32/Lib/win32rcparser.py | 1 + win32/scripts/regsetup.py | 7 +- win32/test/test_win32clipboard.py | 5 +- win32/test/test_win32cred.py | 1 - 47 files changed, 202 insertions(+), 268 deletions(-) diff --git a/AutoDuck/Dump2HHC.py b/AutoDuck/Dump2HHC.py index 343bdbca1..32c728c9d 100644 --- a/AutoDuck/Dump2HHC.py +++ b/AutoDuck/Dump2HHC.py @@ -293,9 +293,7 @@ def _genItemsFromDict(dict, cat, output, target, do_children=1): - """.format( - **locals() - ) + """.format(**locals()) ) if not do_children: continue @@ -339,9 +337,7 @@ def genTOC(cats, output, title, target):
    -""".format( - **locals() - ) +""".format(**locals()) ) for cat in cats: @@ -355,9 +351,7 @@ def genTOC(cats, output, title, target):
      - """.format( - **locals() - ) + """.format(**locals()) ) # Next write the overviews for this category output.write( @@ -368,9 +362,7 @@ def genTOC(cats, output, title, target):
        - """.format( - **locals() - ) + """.format(**locals()) ) _genItemsFromDict(cat.overviewTopics, cat, output, target) _genItemsFromDict(cat.extOverviewTopics, cat, output, target) @@ -387,9 +379,7 @@ def genTOC(cats, output, title, target):
          -""".format( - **locals() - ) +""".format(**locals()) ) _genItemsFromDict(cat.modules, cat, output, target) output.write( @@ -404,9 +394,7 @@ def genTOC(cats, output, title, target): -
            """.format( - **locals() - ) +
              """.format(**locals()) ) # Don't show 'children' for objects - params etc don't need their own child nodes! _genItemsFromDict(cat.objects, cat, output, target, do_children=0) @@ -423,9 +411,7 @@ def genTOC(cats, output, title, target):
                -""".format( - **locals() - ) +""".format(**locals()) ) _genItemsFromDict(cat.constants, cat, output, target) output.write( diff --git a/Pythonwin/pywin/dialogs/list.py b/Pythonwin/pywin/dialogs/list.py index 4e1877955..377a7cade 100644 --- a/Pythonwin/pywin/dialogs/list.py +++ b/Pythonwin/pywin/dialogs/list.py @@ -44,14 +44,17 @@ def OnListClick(self, id, code): return 1 def OnListItemChange(self, std, extra): - (hwndFrom, idFrom, code), ( - itemNotify, - sub, - newState, - oldState, - change, - point, - lparam, + ( + (hwndFrom, idFrom, code), + ( + itemNotify, + sub, + newState, + oldState, + change, + point, + lparam, + ), ) = (std, extra) oldSel = (oldState & commctrl.LVIS_SELECTED) != 0 newSel = (newState & commctrl.LVIS_SELECTED) != 0 diff --git a/Pythonwin/pywin/framework/app.py b/Pythonwin/pywin/framework/app.py index 4e0f09b94..9899e62fa 100644 --- a/Pythonwin/pywin/framework/app.py +++ b/Pythonwin/pywin/framework/app.py @@ -2,7 +2,8 @@ # The application is responsible for managing the main frame window. # # We also grab the FileOpen command, to invoke our Python editor -" The PythonWin application code. Manages most aspects of MDI, etc " +"The PythonWin application code. Manages most aspects of MDI, etc" + from __future__ import annotations import os diff --git a/Pythonwin/pywin/framework/scriptutils.py b/Pythonwin/pywin/framework/scriptutils.py index fe57113dd..32811190b 100644 --- a/Pythonwin/pywin/framework/scriptutils.py +++ b/Pythonwin/pywin/framework/scriptutils.py @@ -27,9 +27,7 @@ debugging_options = """No debugging Step-through in the debugger Run in the debugger -Post-Mortem of unhandled exceptions""".split( - "\n" -) +Post-Mortem of unhandled exceptions""".split("\n") byte_cr = b"\r" byte_lf = b"\n" diff --git a/Pythonwin/pywin/framework/stdin.py b/Pythonwin/pywin/framework/stdin.py index 1614fbe91..b7d71dc19 100644 --- a/Pythonwin/pywin/framework/stdin.py +++ b/Pythonwin/pywin/framework/stdin.py @@ -16,6 +16,7 @@ import sys sys.stdin = sys.stdin.real_file """ + import sys get_input_line = input diff --git a/Pythonwin/pywin/mfc/activex.py b/Pythonwin/pywin/mfc/activex.py index f3d9939a4..3d0207aca 100644 --- a/Pythonwin/pywin/mfc/activex.py +++ b/Pythonwin/pywin/mfc/activex.py @@ -1,5 +1,4 @@ -"""Support for ActiveX control hosting in Pythonwin. -""" +"""Support for ActiveX control hosting in Pythonwin.""" import win32ui import win32uiole diff --git a/adodbapi/examples/db_print.py b/adodbapi/examples/db_print.py index c0eb83ee4..fc86a8b3c 100644 --- a/adodbapi/examples/db_print.py +++ b/adodbapi/examples/db_print.py @@ -1,4 +1,4 @@ -""" db_print.py -- a simple demo for ADO database reads.""" +"""db_print.py -- a simple demo for ADO database reads.""" import sys diff --git a/adodbapi/examples/db_table_names.py b/adodbapi/examples/db_table_names.py index 907bdb85e..17ff58349 100644 --- a/adodbapi/examples/db_table_names.py +++ b/adodbapi/examples/db_table_names.py @@ -1,4 +1,4 @@ -""" db_table_names.py -- a simple demo for ADO database table listing.""" +"""db_table_names.py -- a simple demo for ADO database table listing.""" import sys diff --git a/adodbapi/process_connect_string.py b/adodbapi/process_connect_string.py index a134ad656..b1d4e4e93 100644 --- a/adodbapi/process_connect_string.py +++ b/adodbapi/process_connect_string.py @@ -1,4 +1,4 @@ -""" a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" +"""a clumsy attempt at a macro language to let the programmer execute code on the server (ex: determine 64bit)""" from . import is64bit diff --git a/adodbapi/test/adodbapitest.py b/adodbapi/test/adodbapitest.py index 1b50e595f..dd8e9196d 100644 --- a/adodbapi/test/adodbapitest.py +++ b/adodbapi/test/adodbapitest.py @@ -1,4 +1,4 @@ -""" Unit tests version 2.6.1.0 for adodbapi""" +"""Unit tests version 2.6.1.0 for adodbapi""" """ adodbapi - A python DB API 2.0 interface to Microsoft ADO @@ -325,15 +325,16 @@ def helpTestDataType( rs = crsr.fetchone() if allowedReturnValues: allowedTypes = tuple([type(aRV) for aRV in allowedReturnValues]) - assert isinstance( - rs[0], allowedTypes - ), 'result type "%s" must be one of %s' % (type(rs[0]), allowedTypes) + assert isinstance(rs[0], allowedTypes), ( + 'result type "%s" must be one of %s' % (type(rs[0]), allowedTypes) + ) else: - assert isinstance( - rs[0], type(pyData) - ), 'result type "%s" must be instance of %s' % ( - type(rs[0]), - type(pyData), + assert isinstance(rs[0], type(pyData)), ( + 'result type "%s" must be instance of %s' + % ( + type(rs[0]), + type(pyData), + ) ) if compareAlmostEqual and DBAPIDataTypeString == "DATETIME": @@ -343,9 +344,9 @@ def helpTestDataType( elif compareAlmostEqual: s = float(pyData) v = float(rs[0]) - assert ( - abs(v - s) / s < 0.00001 - ), "Values not almost equal recvd=%s, expected=%f" % (rs[0], s) + assert abs(v - s) / s < 0.00001, ( + "Values not almost equal recvd=%s, expected=%f" % (rs[0], s) + ) else: if allowedReturnValues: ok = False @@ -487,7 +488,7 @@ def testDataTypeDate(self): ) def testDataTypeBinary(self): - binfld = b"\x07\x00\xE2\x40*" + binfld = b"\x07\x00\xe2\x40*" arv = [binfld, adodbapi.Binary(binfld), bytes(binfld)] if self.getEngine() == "PostgreSQL": self.helpTestDataType( @@ -692,9 +693,9 @@ def testRowIterator(self): rec = crsr.fetchone() # check that stepping through an emulated row works for j in range(len(inParam)): - assert ( - rec[j] == inParam[j] - ), 'returned value:"%s" != test value:"%s"' % (rec[j], inParam[j]) + assert rec[j] == inParam[j], ( + 'returned value:"%s" != test value:"%s"' % (rec[j], inParam[j]) + ) # check that we can get a complete tuple from a row assert ( tuple(rec) == inParam diff --git a/adodbapi/test/dbapi20.py b/adodbapi/test/dbapi20.py index 5639e4c3c..373b984d6 100644 --- a/adodbapi/test/dbapi20.py +++ b/adodbapi/test/dbapi20.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -""" Python DB API 2.0 driver compliance unit test suite. +"""Python DB API 2.0 driver compliance unit test suite. - This software is Public Domain and may be used without restrictions. + This software is Public Domain and may be used without restrictions. - "Now we have booze and barflies entering the discussion, plus rumours of - DBAs on drugs... and I won't tell you what flashes through my mind each - time I read the subject line with 'Anal Compliance' in it. All around - this is turning out to be a thoroughly unwholesome unit test." +"Now we have booze and barflies entering the discussion, plus rumours of + DBAs on drugs... and I won't tell you what flashes through my mind each + time I read the subject line with 'Anal Compliance' in it. All around + this is turning out to be a thoroughly unwholesome unit test." - -- Ian Bicking + -- Ian Bicking """ __version__ = "$Revision: 1.15.0 $"[11:-2] diff --git a/adodbapi/test/setuptestframework.py b/adodbapi/test/setuptestframework.py index 65b99e990..e09549421 100644 --- a/adodbapi/test/setuptestframework.py +++ b/adodbapi/test/setuptestframework.py @@ -1,6 +1,7 @@ #!/usr/bin/python2 # Configure this in order to run the testcases. "setuptestframework.py v 2.6.0.8" + import os import shutil import tempfile diff --git a/com/win32com/client/combrowse.py b/com/win32com/client/combrowse.py index f83aa9c9b..4587a9aab 100644 --- a/com/win32com/client/combrowse.py +++ b/com/win32com/client/combrowse.py @@ -1,25 +1,25 @@ """A utility for browsing COM objects. - Usage: +Usage: - Command Prompt + Command Prompt - Use the command *"python.exe combrowse.py"*. This will display - display a fairly small, modal dialog. + Use the command *"python.exe combrowse.py"*. This will display + display a fairly small, modal dialog. - Pythonwin + Pythonwin - Use the "Run Script" menu item, and this will create the browser in an - MDI window. This window can be fully resized. + Use the "Run Script" menu item, and this will create the browser in an + MDI window. This window can be fully resized. - Details +Details - This module allows browsing of registered Type Libraries, COM categories, - and running COM objects. The display is similar to the Pythonwin object - browser, and displays the objects in a hierarchical window. + This module allows browsing of registered Type Libraries, COM categories, + and running COM objects. The display is similar to the Pythonwin object + browser, and displays the objects in a hierarchical window. - Note that this module requires the win32ui (ie, Pythonwin) distribution to - work. + Note that this module requires the win32ui (ie, Pythonwin) distribution to + work. """ @@ -581,7 +581,6 @@ def GetSubList(self): def main(modal=True, mdi=False): - root = HLIRoot("COM Browser") if mdi and "pywin.framework.app" in sys.modules: # do it in a MDI window diff --git a/com/win32com/client/dynamic.py b/com/win32com/client/dynamic.py index 7a269d514..3879aed2c 100644 --- a/com/win32com/client/dynamic.py +++ b/com/win32com/client/dynamic.py @@ -389,9 +389,10 @@ def _get_good_object_(self, ob, userName=None, ReturnCLSID=None): elif isinstance(ob, tuple): return tuple( map( - lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_( - o, oun, rc - ), + lambda o, + s=self, + oun=userName, + rc=ReturnCLSID: s._get_good_single_object_(o, oun, rc), ob, ) ) diff --git a/com/win32com/client/genpy.py b/com/win32com/client/genpy.py index b11d3b85b..c6d8b4319 100644 --- a/com/win32com/client/genpy.py +++ b/com/win32com/client/genpy.py @@ -969,9 +969,7 @@ def _Build_Interface(self, type_info_tuple): return oleItem, vtableItem def BuildOleItemsFromType(self): - assert ( - self.bBuildHidden - ), "This code doesn't look at the hidden flag - I thought everyone set it true!?!?!" + assert self.bBuildHidden, "This code doesn't look at the hidden flag - I thought everyone set it true!?!?!" oleItems = {} enumItems = {} recordItems = {} diff --git a/com/win32com/client/makepy.py b/com/win32com/client/makepy.py index 9f05ab5b5..4d60856ed 100644 --- a/com/win32com/client/makepy.py +++ b/com/win32com/client/makepy.py @@ -15,11 +15,12 @@ """Generate a .py file from an OLE TypeLibrary file. - This module is concerned only with the actual writing of - a .py file. It draws on the @build@ module, which builds - the knowledge of a COM interface. +This module is concerned only with the actual writing of +a .py file. It draws on the @build@ module, which builds +the knowledge of a COM interface. """ + usageHelp = """ \ Usage: diff --git a/com/win32com/client/selecttlb.py b/com/win32com/client/selecttlb.py index bf8627f5f..1d10c9ad8 100644 --- a/com/win32com/client/selecttlb.py +++ b/com/win32com/client/selecttlb.py @@ -1,5 +1,4 @@ -"""Utilities for selecting and enumerating the Type Libraries installed on the system -""" +"""Utilities for selecting and enumerating the Type Libraries installed on the system""" import pythoncom import win32api diff --git a/com/win32com/makegw/makegw.py b/com/win32com/makegw/makegw.py index 61e48ab37..2a12239c8 100644 --- a/com/win32com/makegw/makegw.py +++ b/com/win32com/makegw/makegw.py @@ -1,48 +1,48 @@ """Utility functions for writing out gateway C++ files - This module will generate a C++/Python binding for a specific COM - interface. - - 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 - server side support for the interface). Many COM interfaces are useful - both as Client and Server. Other interfaces, however, really only make - sense to implement one side or the other. For example, it would be pointless - for Python to implement Server side for 'IRunningObjectTable', unless we were - implementing core COM for an operating system in Python (hey - now there's an idea!) - - Most COM interface code is totally boiler-plate - it consists of - converting arguments, dispatching the call to Python, and processing - any result values. - - This module automates the generation of such code. It has the ability to - parse a .H file generated by the MIDL tool (ie, almost all COM .h files) - and build almost totally complete C++ code. - - The module understands some of the well known data types, and how to - convert them. There are only a couple of places where hand-editing is - necessary, as detailed below: - - unsupported types -- If a type is not known, the generator will - pretty much ignore it, but write a comment to the generated code. You - may want to add custom support for this type. In some cases, C++ compile errors - will result. These are intentional - generating code to remove these errors would - imply a false sense of security that the generator has done the right thing. - - other return policies -- By default, Python never sees the return SCODE from - a COM function. The interface usually returns None if OK, else a COM exception - if "FAILED(scode)" is TRUE. You may need to change this if: - * EXCEPINFO is passed to the COM function. This is not detected and handled - * For some reason Python should always see the result SCODE, even if it - did fail or succeed. For example, some functions return a BOOLEAN result - in the SCODE, meaning Python should always see it. - * FAILED(scode) for the interface still has valid data to return (by default, - the code generated does not process the return values, and raise an exception - to Python/COM +This module will generate a C++/Python binding for a specific COM +interface. + +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 +server side support for the interface). Many COM interfaces are useful +both as Client and Server. Other interfaces, however, really only make +sense to implement one side or the other. For example, it would be pointless +for Python to implement Server side for 'IRunningObjectTable', unless we were +implementing core COM for an operating system in Python (hey - now there's an idea!) + +Most COM interface code is totally boiler-plate - it consists of +converting arguments, dispatching the call to Python, and processing +any result values. + +This module automates the generation of such code. It has the ability to +parse a .H file generated by the MIDL tool (ie, almost all COM .h files) +and build almost totally complete C++ code. + +The module understands some of the well known data types, and how to +convert them. There are only a couple of places where hand-editing is +necessary, as detailed below: + +unsupported types -- If a type is not known, the generator will +pretty much ignore it, but write a comment to the generated code. You +may want to add custom support for this type. In some cases, C++ compile errors +will result. These are intentional - generating code to remove these errors would +imply a false sense of security that the generator has done the right thing. + +other return policies -- By default, Python never sees the return SCODE from +a COM function. The interface usually returns None if OK, else a COM exception +if "FAILED(scode)" is TRUE. You may need to change this if: +* EXCEPINFO is passed to the COM function. This is not detected and handled +* For some reason Python should always see the result SCODE, even if it + did fail or succeed. For example, some functions return a BOOLEAN result + in the SCODE, meaning Python should always see it. +* FAILED(scode) for the interface still has valid data to return (by default, + the code generated does not process the return values, and raise an exception + to Python/COM """ import argparse @@ -202,9 +202,7 @@ def _write_ifc_cpp(f, interface): return ({name} *)Py{base}::GetI(self); }} -""".format( - **interface.__dict__ - ) +""".format(**interface.__dict__) ) ptr = re.sub(r"[a-z]", "", interface.name) @@ -219,9 +217,7 @@ def _write_ifc_cpp(f, interface): {interfacename} *p{ptr} = GetI(self); if ( p{ptr} == NULL ) return NULL; -""".format( - **strdict - ) +""".format(**strdict) ) argsParseTuple = argsCOM = formatChars = codePost = codePobjects = ( codeCobjects @@ -294,9 +290,7 @@ def _write_ifc_cpp(f, interface): {cleanup_gil} if ( FAILED(hr) ) return PyCom_BuildPyException(hr, p{ptr}, IID_{interfacename} ); -""".format( - **strdict - ) +""".format(**strdict) ) codePre = codePost = formatChars = codeVarsPass = codeDecl = "" for arg in method.args: @@ -348,9 +342,7 @@ def _write_ifc_cpp(f, interface): sizeof(Py{name}), Py{name}_methods, GET_PYCOM_CTOR(Py{name})); -""".format( - **locals() - ) +""".format(**locals()) ) diff --git a/com/win32com/makegw/makegwenum.py b/com/win32com/makegw/makegwenum.py index 3ba274a4e..d3698af75 100644 --- a/com/win32com/makegw/makegwenum.py +++ b/com/win32com/makegw/makegwenum.py @@ -191,9 +191,7 @@ def _write_enumifc_cpp(f, interface): sizeof(PyIEnum{enumtype}), PyIEnum{enumtype}_methods, GET_PYCOM_CTOR(PyIEnum{enumtype})); -""".format( - **locals() - ) +""".format(**locals()) ) @@ -329,7 +327,5 @@ def _write_enumgw_cpp(f, interface): return PyCom_CheckIEnumNextResult(hr, IID_IEnum{enumtype}); }} -""".format( - **locals() - ) +""".format(**locals()) ) diff --git a/com/win32com/makegw/makegwparse.py b/com/win32com/makegw/makegwparse.py index 89a574608..14e800db6 100644 --- a/com/win32com/makegw/makegwparse.py +++ b/com/win32com/makegw/makegwparse.py @@ -1,15 +1,15 @@ """Utilities for makegw - Parse a header file to build an interface - This module contains the core code for parsing a header file describing a - COM interface, and building it into an "Interface" structure. +This module contains the core code for parsing a header file describing a +COM interface, and building it into an "Interface" structure. - Each Interface has methods, and each method has arguments. +Each Interface has methods, and each method has arguments. - Each argument knows how to use Py_BuildValue or Py_ParseTuple to - exchange itself with Python. +Each argument knows how to use Py_BuildValue or Py_ParseTuple to +exchange itself with Python. - See the @win32com.makegw@ module for information in building a COM - interface +See the @win32com.makegw@ module for information in building a COM +interface """ from __future__ import annotations diff --git a/com/win32com/olectl.py b/com/win32com/olectl.py index f6be98bda..3ec5bf6ef 100644 --- a/com/win32com/olectl.py +++ b/com/win32com/olectl.py @@ -1,6 +1,6 @@ """Constants used by COM Controls - Hand created version of OLECTL.H constants. +Hand created version of OLECTL.H constants. """ import winerror diff --git a/com/win32com/server/connect.py b/com/win32com/server/connect.py index e99555a04..1fe7a0313 100644 --- a/com/win32com/server/connect.py +++ b/com/win32com/server/connect.py @@ -1,6 +1,6 @@ """Utilities for Server Side connections. - A collection of helpers for server side connection points. +A collection of helpers for server side connection points. """ import pythoncom diff --git a/com/win32com/server/exception.py b/com/win32com/server/exception.py index a6b3111fe..c31073e1c 100644 --- a/com/win32com/server/exception.py +++ b/com/win32com/server/exception.py @@ -1,15 +1,15 @@ """Exception Handling - Exceptions +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. + 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 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. + This module provides a class which provides the necessary attributes. """ import sys diff --git a/com/win32com/servers/interp.py b/com/win32com/servers/interp.py index 37d83bd49..0676e2127 100644 --- a/com/win32com/servers/interp.py +++ b/com/win32com/servers/interp.py @@ -1,14 +1,14 @@ """Python.Interpreter COM Server - This module implements a very very simple COM server which - exposes the Python interpreter. +This module implements a very very simple COM server which +exposes the Python interpreter. - This is designed more as a demonstration than a full blown COM server. - General functionality and Error handling are both limited. +This is designed more as a demonstration than a full blown COM server. +General functionality and Error handling are both limited. - To use this object, ensure it is registered by running this module - from Python.exe. Then, from Visual Basic, use "CreateObject('Python.Interpreter')", - and call its methods! +To use this object, ensure it is registered by running this module +from Python.exe. Then, from Visual Basic, use "CreateObject('Python.Interpreter')", +and call its methods! """ import winerror diff --git a/com/win32com/test/errorSemantics.py b/com/win32com/test/errorSemantics.py index 843395c41..fe5045cc8 100644 --- a/com/win32com/test/errorSemantics.py +++ b/com/win32com/test/errorSemantics.py @@ -158,7 +158,7 @@ def test(): "The source in the exception tuple did not yield the correct string", str(com_exc), ) - assert exc[2] == "\U0001F600", ( + assert exc[2] == "\U0001f600", ( "The description in the exception tuple did not yield the correct string", str(com_exc), ) @@ -213,7 +213,7 @@ def testLogger(): except pythoncom.error as exc: # `excepinfo` is a tuple with elt 2 being the traceback we captured. message = exc.excepinfo[2] - assert message.endswith("Exception: \U0001F600\n") + assert message.endswith("Exception: \U0001f600\n") assert handler.num_emits == 1, handler.num_emits assert handler.last_record.startswith( "pythoncom error: Unexpected exception in gateway method 'Commit'" @@ -228,7 +228,7 @@ def testLogger(): except pythoncom.error as exc: # `excepinfo` is a tuple with elt 2 being the traceback we captured. message = exc.excepinfo[2] - assert message.endswith("Exception: \U0001F600\n") + assert message.endswith("Exception: \U0001f600\n") assert handler.num_emits == 1, handler.num_emits handler.reset() diff --git a/com/win32com/test/testExchange.py b/com/win32com/test/testExchange.py index 119c16a17..f54422bb6 100644 --- a/com/win32com/test/testExchange.py +++ b/com/win32com/test/testExchange.py @@ -91,7 +91,6 @@ def TestUser(session): def test(): - oldcwd = os.getcwd() try: session = gencache.EnsureDispatch("MAPI.Session") diff --git a/com/win32com/test/testvb.py b/com/win32com/test/testvb.py index ca5ab7689..65426559d 100644 --- a/com/win32com/test/testvb.py +++ b/com/win32com/test/testvb.py @@ -95,10 +95,13 @@ def TestVB(vbtest, bUseGenerated): vbtest.VariantProperty == "Hello from Python" ), "Could not set the variant string property correctly." vbtest.VariantProperty = (1.0, 2.0, 3.0) - assert vbtest.VariantProperty == ( - 1.0, - 2.0, - 3.0, + assert ( + vbtest.VariantProperty + == ( + 1.0, + 2.0, + 3.0, + ) ), f"Could not set the variant property to an array of floats correctly - '{vbtest.VariantProperty}'." TestArrays(vbtest, bUseGenerated) @@ -195,8 +198,8 @@ def _getcount(ob): check = [] for item in i: check.append(item) - assert check == list( - expected + assert ( + check == list(expected) ), f"Collection iterator {col_name} didn't have {expected!r} 2nd time around (had {check!r})" # but an iterator is not restartable check = [] diff --git a/com/win32com/util.py b/com/win32com/util.py index 0a5748f1e..2fa8931af 100644 --- a/com/win32com/util.py +++ b/com/win32com/util.py @@ -1,6 +1,6 @@ """General utility functions common to client and server. - This module contains a collection of general purpose utility functions. +This module contains a collection of general purpose utility functions. """ import pythoncom diff --git a/com/win32comext/axdebug/adb.py b/com/win32comext/axdebug/adb.py index 191041809..ee6b620c5 100644 --- a/com/win32comext/axdebug/adb.py +++ b/com/win32comext/axdebug/adb.py @@ -1,5 +1,4 @@ -"""The glue between the Python debugger interface and the Active Debugger interface -""" +"""The glue between the Python debugger interface and the Active Debugger interface""" import _thread import bdb diff --git a/com/win32comext/axdebug/contexts.py b/com/win32comext/axdebug/contexts.py index df6e97760..d732fbae1 100644 --- a/com/win32comext/axdebug/contexts.py +++ b/com/win32comext/axdebug/contexts.py @@ -1,6 +1,4 @@ -""" A module for managing the AXDebug I*Contexts - -""" +"""A module for managing the AXDebug I*Contexts""" from . import adb, axdebug, gateways diff --git a/com/win32comext/axdebug/documents.py b/com/win32comext/axdebug/documents.py index 560901fb4..eb45517e7 100644 --- a/com/win32comext/axdebug/documents.py +++ b/com/win32comext/axdebug/documents.py @@ -1,5 +1,4 @@ -""" Management of documents for AXDebugging. -""" +"""Management of documents for AXDebugging.""" import pythoncom import win32api diff --git a/com/win32comext/axdebug/dump.py b/com/win32comext/axdebug/dump.py index 61a1cfc7a..6500167c0 100644 --- a/com/win32comext/axdebug/dump.py +++ b/com/win32comext/axdebug/dump.py @@ -46,9 +46,7 @@ def dumpall(): e = Enumerator(dm.EnumApplications()) for app in e: print(f"Application: {app.GetName()}") - node = ( - app.GetRootNode() - ) # of type PyIDebugApplicationNode->PyIDebugDocumentProvider->PyIDebugDocumentInfo + node = app.GetRootNode() # of type PyIDebugApplicationNode->PyIDebugDocumentProvider->PyIDebugDocumentInfo DumpDebugApplicationNode(node) diff --git a/com/win32comext/axscript/client/error.py b/com/win32comext/axscript/client/error.py index 5aadbf9bc..f02e66603 100644 --- a/com/win32comext/axscript/client/error.py +++ b/com/win32comext/axscript/client/error.py @@ -1,7 +1,7 @@ """Exception and error handling. - This contains the core exceptions that the implementations should raise - as well as the IActiveScriptError interface code. +This contains the core exceptions that the implementations should raise +as well as the IActiveScriptError interface code. """ from __future__ import annotations diff --git a/com/win32comext/axscript/client/framework.py b/com/win32comext/axscript/client/framework.py index b9f0fd605..c4a04329f 100644 --- a/com/win32comext/axscript/client/framework.py +++ b/com/win32comext/axscript/client/framework.py @@ -1,10 +1,10 @@ """AXScript Client Framework - This module provides a core framework for an ActiveX Scripting client. - Derived classes actually implement the AX Client itself, including the - scoping rules, etc. +This module provides a core framework for an ActiveX Scripting client. +Derived classes actually implement the AX Client itself, including the +scoping rules, etc. - There are classes defined for the engine itself, and for ScriptItems +There are classes defined for the engine itself, and for ScriptItems """ from __future__ import annotations diff --git a/com/win32comext/axscript/client/scriptdispatch.py b/com/win32comext/axscript/client/scriptdispatch.py index ca696fe84..6bd29adcb 100644 --- a/com/win32comext/axscript/client/scriptdispatch.py +++ b/com/win32comext/axscript/client/scriptdispatch.py @@ -1,8 +1,8 @@ """dynamic dispatch objects for AX Script. - This is an IDispatch object that a scripting host may use to - query and invoke methods on the main script. Not may hosts use - this yet, so it is not well tested! +This is an IDispatch object that a scripting host may use to +query and invoke methods on the main script. Not may hosts use +this yet, so it is not well tested! """ from __future__ import annotations diff --git a/isapi/samples/redirector_with_filter.py b/isapi/samples/redirector_with_filter.py index 6d9914c04..393e89d05 100644 --- a/isapi/samples/redirector_with_filter.py +++ b/isapi/samples/redirector_with_filter.py @@ -84,6 +84,7 @@ def Dispatch(self, ecb): # The ISAPI filter. class Filter(SimpleFilter): "Sample Python Redirector" + filter_flags = isapicon.SF_NOTIFY_PREPROC_HEADERS | isapicon.SF_NOTIFY_ORDER_DEFAULT def HttpFilterProc(self, fc): diff --git a/isapi/simple.py b/isapi/simple.py index d5fcb04d7..177e8935c 100644 --- a/isapi/simple.py +++ b/isapi/simple.py @@ -40,6 +40,7 @@ def TerminateExtension(self, status): class SimpleFilter: "Base class for a a simple ISAPI filter" + filter_flags: int | None = None def __init__(self): diff --git a/isapi/threaded_extension.py b/isapi/threaded_extension.py index 044b90987..b6a161a23 100644 --- a/isapi/threaded_extension.py +++ b/isapi/threaded_extension.py @@ -60,6 +60,7 @@ def call_handler(self, cblock): # fully asynch extension. class ThreadPoolExtension(isapi.simple.SimpleExtension): "Base class for an ISAPI extension based around a thread-pool" + max_workers = 20 worker_shutdown_wait = 15000 # 15 seconds for workers to quit... diff --git a/setup.py b/setup.py index e25465fd7..e72edf0ad 100644 --- a/setup.py +++ b/setup.py @@ -1341,9 +1341,7 @@ def finalize_options(self): {win32com}/extensions/PyICancelMethodCalls.cpp {win32com}/extensions/PyIContext.cpp {win32com}/extensions/PyIEnumContextProps.cpp {win32com}/extensions/PyIClientSecurity.cpp {win32com}/extensions/PyIServerSecurity.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), depends=( """ @@ -1370,9 +1368,7 @@ def finalize_options(self): {win32com}/include\\PyICancelMethodCalls.h {win32com}/include\\PyIContext.h {win32com}/include\\PyIEnumContextProps.h {win32com}/include\\PyIClientSecurity.h {win32com}/include\\PyIServerSecurity.h - """.format( - **dirs - ) + """.format(**dirs) ).split(), libraries="oleaut32 ole32 user32 urlmon", export_symbol_file="com/win32com/src/PythonCOM.def", @@ -1400,9 +1396,7 @@ def finalize_options(self): {adsi}/adsilib.i {adsi}/PyADSIUtil.cpp {adsi}/PyDSOPObjects.cpp {adsi}/PyIADs.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1420,9 +1414,7 @@ def finalize_options(self): {axcontrol}/PyIOleClientSite.cpp {axcontrol}/PyIOleInPlaceSite.cpp {axcontrol}/PyIOleObject.cpp {axcontrol}/PyIViewObject2.cpp {axcontrol}/PyIOleCommandTarget.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1437,9 +1429,7 @@ def finalize_options(self): {axscript}/PyIActiveScriptParse.cpp {axscript}/PyIActiveScriptParseProcedure.cpp {axscript}/PyIActiveScriptSite.cpp {axscript}/PyIMultiInfos.cpp {axscript}/PyIObjectSafety.cpp {axscript}/stdafx.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), depends=( """ @@ -1448,9 +1438,7 @@ def finalize_options(self): {axscript}/PyIActiveScriptError.h {axscript}/PyIObjectSafety.h {axscript}/PyIProvideMultipleClassInfo.h {axscript}/stdafx.h - """.format( - **dirs - ) + """.format(**dirs) ).split(), extra_compile_args=["-DPY_BUILD_AXSCRIPT"], implib_name="axscript", @@ -1506,9 +1494,7 @@ def finalize_options(self): {axdebug}/PyIRemoteDebugApplicationEvents.cpp {axdebug}/PyIRemoteDebugApplicationThread.cpp {axdebug}/stdafx.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1521,9 +1507,7 @@ def finalize_options(self): {internet}/PyIInternetPriority.cpp {internet}/PyIInternetProtocol.cpp {internet}/PyIInternetProtocolInfo.cpp {internet}/PyIInternetProtocolRoot.cpp {internet}/PyIInternetProtocolSink.cpp {internet}/PyIInternetSecurityManager.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), depends=["{internet}/internet_pch.h".format(**dirs)], ), @@ -1559,9 +1543,7 @@ def finalize_options(self): {mapi}/mapiguids.cpp {mapi}/MAPIStubLibrary/library/mapiStubLibrary.cpp {mapi}/MAPIStubLibrary/library/stubutils.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com_mapi( @@ -1577,9 +1559,7 @@ def finalize_options(self): {mapi}/exchangeguids.cpp {mapi}/MAPIStubLibrary/library/mapiStubLibrary.cpp {mapi}/MAPIStubLibrary/library/stubutils.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1661,9 +1641,7 @@ def finalize_options(self): {shell}/PyIUniformResourceLocator.cpp {shell}/shell.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1691,9 +1669,7 @@ def finalize_options(self): {propsys}/PyIObjectWithPropertyKey.cpp {propsys}/PyIPropertyChange.cpp {propsys}/PyIPropertyChangeArray.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), implib_name="pypropsys", ), @@ -1709,9 +1685,7 @@ def finalize_options(self): {taskscheduler}/PyITaskScheduler.cpp {taskscheduler}/PyITaskTrigger.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1732,9 +1706,7 @@ def finalize_options(self): {bits}/PyIEnumBackgroundCopyJobs.cpp {bits}/PyIEnumBackgroundCopyFiles.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), WinExt_win32com( @@ -1755,18 +1727,14 @@ def finalize_options(self): {directsound}/PyIDirectSoundBuffer.cpp {directsound}/PyIDirectSoundCapture.cpp {directsound}/PyIDirectSoundCaptureBuffer.cpp {directsound}/PyIDirectSoundNotify.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), depends=( """ {directsound}/directsound_pch.h {directsound}/PyIDirectSound.h {directsound}/PyIDirectSoundBuffer.h {directsound}/PyIDirectSoundCapture.h {directsound}/PyIDirectSoundCaptureBuffer.h {directsound}/PyIDirectSoundNotify.h - """.format( - **dirs - ) + """.format(**dirs) ).split(), optional_headers=["dsound.h"], libraries="user32 dsound dxguid", @@ -1778,9 +1746,7 @@ def finalize_options(self): """ {authorization}/authorization.cpp {authorization}/PyGSecurityInformation.cpp - """.format( - **dirs - ) + """.format(**dirs) ).split(), ), ] diff --git a/win32/Demos/security/GetTokenInformation.py b/win32/Demos/security/GetTokenInformation.py index 192c549e1..745e0e78e 100644 --- a/win32/Demos/security/GetTokenInformation.py +++ b/win32/Demos/security/GetTokenInformation.py @@ -1,5 +1,5 @@ -""" Lists various types of information about current user's access token, - including UAC status on Vista +"""Lists various types of information about current user's access token, +including UAC status on Vista """ import pywintypes diff --git a/win32/Demos/win32ts_logoff_disconnected.py b/win32/Demos/win32ts_logoff_disconnected.py index 2a8bcc940..3b7f882e0 100644 --- a/win32/Demos/win32ts_logoff_disconnected.py +++ b/win32/Demos/win32ts_logoff_disconnected.py @@ -1,4 +1,4 @@ -""" Finds any disconnected terminal service sessions and logs them off""" +"""Finds any disconnected terminal service sessions and logs them off""" import pywintypes import win32ts diff --git a/win32/Lib/win32evtlogutil.py b/win32/Lib/win32evtlogutil.py index 05cabe2b8..58d3326ef 100644 --- a/win32/Lib/win32evtlogutil.py +++ b/win32/Lib/win32evtlogutil.py @@ -1,5 +1,4 @@ -"""Event Log Utilities - helper for win32evtlog.pyd -""" +"""Event Log Utilities - helper for win32evtlog.pyd""" import win32api import win32con diff --git a/win32/Lib/win32gui_struct.py b/win32/Lib/win32gui_struct.py index a4b7a4f4c..5651c7bc8 100644 --- a/win32/Lib/win32gui_struct.py +++ b/win32/Lib/win32gui_struct.py @@ -808,9 +808,7 @@ def EmptyLVCOLUMN(mask=None, text_buf_size=512): text_addr, _ = text_buffer.buffer_info() else: text_addr = text_buf_size = 0 - buf = struct.pack( - _lvcolumn_fmt, mask, 0, 0, text_addr, text_buf_size, 0, 0, 0 - ) + buf = struct.pack(_lvcolumn_fmt, mask, 0, 0, text_addr, text_buf_size, 0, 0, 0) return array.array("b", buf), extra diff --git a/win32/Lib/win32rcparser.py b/win32/Lib/win32rcparser.py index b87bd96fe..183f075b1 100644 --- a/win32/Lib/win32rcparser.py +++ b/win32/Lib/win32rcparser.py @@ -7,6 +7,7 @@ This is a parser for Windows .rc files, which are text files which define dialogs and other Windows UI resources. """ + from __future__ import annotations import os diff --git a/win32/scripts/regsetup.py b/win32/scripts/regsetup.py index da2f870e1..fb33e11dd 100644 --- a/win32/scripts/regsetup.py +++ b/win32/scripts/regsetup.py @@ -447,8 +447,7 @@ def RegisterShellInfo(searchPaths): # FindRegisterApp("win32", ["win32con.pyc", "win32api%s.pyd" % suffix], searchPaths) -usage = ( - """\ +usage = """\ regsetup.py - Setup/maintain the registry for Python apps. Run without options, (but possibly search paths) to repair a totally broken @@ -475,9 +474,7 @@ def RegisterShellInfo(searchPaths): --description -- Print a description of the usage. --examples -- Print examples of usage. -""" - % sys.argv[0] -) +""" % sys.argv[0] description = """\ If no options are processed, the program attempts to validate and set diff --git a/win32/test/test_win32clipboard.py b/win32/test/test_win32clipboard.py index f5d5c28d1..ccfc32e53 100644 --- a/win32/test/test_win32clipboard.py +++ b/win32/test/test_win32clipboard.py @@ -5,7 +5,6 @@ class TestGetSetClipboardData(unittest.TestCase): - def copyData(self, data, format_): win32clipboard.OpenClipboard() ret = None @@ -30,13 +29,13 @@ 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, + 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,), + b"Dummy\x00\xff bytes": (win32clipboard.CF_UNICODETEXT,), } for data, formats in test_data.items(): for fmt in formats: diff --git a/win32/test/test_win32cred.py b/win32/test/test_win32cred.py index c44fe18b8..4b9fe08d7 100644 --- a/win32/test/test_win32cred.py +++ b/win32/test/test_win32cred.py @@ -5,7 +5,6 @@ class TestCredFunctions(unittest.TestCase): - def setUp(self): self.flags = 0 self.dummy_cred = { From 5a46a21be0fdf9472f1904c54bd301745cfc482d Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 14 Oct 2024 13:21:36 -0400 Subject: [PATCH 4/4] Update git-blame-ignore-revs --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4816d046a..9309054aa 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -13,3 +13,6 @@ # 2024-05-28 formatted c++ source with clang-format 637448f8252ab142eedd539ddf9b08259b73eecc + +# 2024-10-14 formatted Python source with Ruff format +2b5191d8fc6f1d1fbde01481b49278c1957ef8f1