Skip to content

Commit

Permalink
Merge branch 'main' into test-free-threaded-builds
Browse files Browse the repository at this point in the history
  • Loading branch information
Avasam authored Oct 18, 2024
2 parents 5dba0b3 + 49c52f9 commit 8a558ea
Show file tree
Hide file tree
Showing 26 changed files with 99 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .github/issue_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ For all bugs, please provide the following information:
## Expected behavior and actual behavior

## Steps to reproduce the problem
<!-- Prefer using code snippets rather than a screenshot. Please include a full minimal reproduction if possible. -->
<!-- Prefer using code snippets rather than a screenshot. Please include a [full minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) if possible. -->

1. ...
2. ...
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/download-arm64-libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
VERSION += f"-rc{sys.version_info.serial}"

URL = f"https://www.nuget.org/api/v2/package/pythonarm64/{VERSION}"
PATH = dest / f"pythonarm64.{VERSION}.zip"
DEST_PATH = dest / f"pythonarm64.{VERSION}.zip"

if PATH.is_file():
print("Skipping download because", PATH, "exists")
if DEST_PATH.is_file():
print("Skipping download because", DEST_PATH, "exists")
else:
print("Downloading", URL)
urlretrieve(URL, PATH)
print("Downloaded", PATH)
urlretrieve(URL, DEST_PATH)
print("Downloaded", DEST_PATH)

with ZipFile(PATH, "r") as zf:
with ZipFile(DEST_PATH, "r") as zf:
for name in zf.namelist():
zip_path = pathlib.PurePath(name)
if zip_path.parts[:2] == ("tools", "libs"):
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
# python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
# python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
# architecture: ["x64", "x86"]
# free-threaded: [false]
include:
Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
run: |
python --version
pip --version
pip install --upgrade setuptools wheel
pip install --upgrade setuptools>=74 wheel
- name: Build and install
run: |
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
run: |
python --version
pip --version
pip install --upgrade setuptools wheel
pip install --upgrade setuptools>=74 wheel
- name: Obtain ARM64 library files
run: |
Expand Down Expand Up @@ -172,7 +172,6 @@ jobs:
# 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", "3.13"]
# steps:
# - uses: actions/checkout@v4
Expand All @@ -191,7 +190,7 @@ jobs:
# strategy:
# fail-fast: false
# matrix:
# python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
# python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-python@v5
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ https://mhammond.github.io/pywin32_installers.html.
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)
* 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)
* Removed temporary `win32com.server.policy` reexports hack (#2344, @Avasam)
Import `DispatcherWin32trace` and `DispatcherTrace` from `win32com.server.dispatcher` instead.
Expand Down
5 changes: 2 additions & 3 deletions Pythonwin/pywin/test/test_pywin.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ def setUpClass(cls):
def _restore_oe():
sys.stdout, sys.stderr = cls.std_oe_orig

if sys.version_info >= (3, 8):
cls.addClassCleanup(_restore_oe)
cls.addClassCleanup(_restore_oe)
sys.argv[1:] = ["/new", src_dir + "\\_dbgscript.py"]
if not _indebugger:
thisApp.InitInstance()
Expand All @@ -65,7 +64,7 @@ def tearDownClass(cls):
win32api.PostQuitMessage()
win32gui.PumpWaitingMessages()
cls.app.ExitInstance()
sys.stdout, sys.stderr = cls.std_oe_orig # py3.7
sys.stdout, sys.stderr = cls.std_oe_orig

def test_1_pydocs_and_finddlg(self):
mf = win32ui.GetMainFrame()
Expand Down
4 changes: 0 additions & 4 deletions build_all.bat
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
py -3.7-32 setup.py -q build
@if errorlevel 1 goto failed
py -3.7 setup.py -q build
@if errorlevel 1 goto failed
py -3.8-32 setup.py -q build
@if errorlevel 1 goto failed
py -3.8 setup.py -q build
Expand Down
1 change: 0 additions & 1 deletion build_env.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ from the pywin32 directory.
- Update `setuptools` and set the following environment variables to ensure it is used:

```shell
set SETUPTOOLS_USE_DISTUTILS=1
set DISTUTILS_USE_SDK=1
```

Expand Down
8 changes: 8 additions & 0 deletions com/TestSources/PyCOMTest/PyCOMImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,14 @@ HRESULT CPyCOMTest::GetStruct(TestStruct1 *ret)
*ret = r;
return S_OK;
}

HRESULT CPyCOMTest::ModifyStruct(TestStruct1 *prec)
{
prec->int_value = 100;
prec->str_value = SysAllocString(L"Nothing is as constant as change");
return S_OK;
}

HRESULT CPyCOMTest::DoubleString(BSTR in, BSTR *out)
{
*out = SysAllocStringLen(NULL, SysStringLen(in) * 2);
Expand Down
2 changes: 2 additions & 0 deletions com/TestSources/PyCOMTest/PyCOMImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class CPyCOMTest : public IDispatchImpl<IPyCOMTest, &IID_IPyCOMTest, &LIBID_PyCO
STDMETHOD(None)();
STDMETHOD(def)();

STDMETHOD(ModifyStruct)(TestStruct1 *prec);

// info associated to each session
struct PyCOMTestSessionData {
IStream *pStream; // Stream for marshalling the data to the new thread.
Expand Down
2 changes: 2 additions & 0 deletions com/TestSources/PyCOMTest/PyCOMTest.idl
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ library PyCOMTestLib
// reserved words etc
HRESULT None();
HRESULT def();
// Test struct byref as [ in, out ] parameter.
HRESULT ModifyStruct([ in, out ] TestStruct1 * prec);
};

// Define a new class to test how Python handles derived interfaces!
Expand Down
1 change: 0 additions & 1 deletion com/win32com/client/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,6 @@ def _ResolveType(typerepr, itypeinfo):
if was_user and subrepr in [
pythoncom.VT_DISPATCH,
pythoncom.VT_UNKNOWN,
pythoncom.VT_RECORD,
]:
# Drop the VT_PTR indirection
return subrepr, sub_clsid, sub_doc
Expand Down
2 changes: 1 addition & 1 deletion com/win32com/client/gencache.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def EnsureDispatch(
): # New fn, so we default the new demand feature to on!
"""Given a COM prog_id, return an object that is using makepy support, building if necessary"""
disp = win32com.client.Dispatch(prog_id)
if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
if not hasattr(disp, "CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
Expand Down
7 changes: 4 additions & 3 deletions com/win32com/src/oleargs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,15 +860,15 @@ static PyObject *PyCom_PyObjectFromSAFEARRAYDimensionItem(SAFEARRAY *psa, VARENU
hres = SafeArrayGetElement(psa, arrayIndices, &str);
if (FAILED(hres))
break;
subitem = PyWinObject_FromBstr(str);
subitem = PyWinObject_FromBstr(str, TRUE);
break;
}
case VT_DISPATCH: {
IDispatch *pDisp;
hres = SafeArrayGetElement(psa, arrayIndices, &pDisp);
if (FAILED(hres))
break;
subitem = PyCom_PyObjectFromIUnknown(pDisp, IID_IDispatch, TRUE);
subitem = PyCom_PyObjectFromIUnknown(pDisp, IID_IDispatch, FALSE);
break;
}
// case VT_ERROR - handled above with I4
Expand All @@ -895,7 +895,7 @@ static PyObject *PyCom_PyObjectFromSAFEARRAYDimensionItem(SAFEARRAY *psa, VARENU
hres = SafeArrayGetElement(psa, arrayIndices, &pUnk);
if (FAILED(hres))
break;
subitem = PyCom_PyObjectFromIUnknown(pUnk, IID_IUnknown, TRUE);
subitem = PyCom_PyObjectFromIUnknown(pUnk, IID_IUnknown, FALSE);
break;
}
// case VT_DECIMAL
Expand Down Expand Up @@ -1572,6 +1572,7 @@ BOOL PythonOleArgHelper::MakeObjToVariant(PyObject *obj, VARIANT *var, PyObject
// Nothing else to do - the code below sets the VT up correctly.
break;
case VT_RECORD:
case VT_RECORD | VT_BYREF:
rc = PyObject_AsVARIANTRecordInfo(obj, var);
break;
case VT_CY:
Expand Down
5 changes: 1 addition & 4 deletions com/win32com/test/pippo_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ def Method3(self, in1):


def BuildTypelib():
if sys.version_info >= (3, 8):
from setuptools.modified import newer
else:
from distutils.dep_util import newer
from setuptools.modified import newer

this_dir = os.path.dirname(__file__)
idl = os.path.abspath(os.path.join(this_dir, "pippo.idl"))
Expand Down
12 changes: 12 additions & 0 deletions com/win32com/test/testPyComTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,18 @@ def TestCommon(o, is_generated):
progress("Checking structs")
r = o.GetStruct()
assert r.int_value == 99 and str(r.str_value) == "Hello from C++"
# Dynamic does not support struct byref as [ in, out ] parameters
if hasattr(o, "CLSID"):
progress("Checking struct byref as [ in, out ] parameter")
mod_r = o.ModifyStruct(r)
# We expect the input value to stay unchanged
assert r.int_value == 99 and str(r.str_value) == "Hello from C++"
# and the return value to reflect the modifications performed on the COM server side
assert (
mod_r.int_value == 100
and str(mod_r.str_value) == "Nothing is as constant as change"
)

assert o.DoubleString("foo") == "foofoo"

progress("Checking var args")
Expand Down
5 changes: 0 additions & 5 deletions make_all.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ rem Now the binaries.
rem Check /build_env.md#build-environment to make sure you have all the required components installed

rem (bdist_wininst needs --target-version to name the installers correctly!)
py -3.7-32 setup.py -q bdist_wininst --skip-build --target-version=3.7
py -3.7-32 setup.py -q bdist_wheel --skip-build
py -3.7 setup.py -q bdist_wininst --skip-build --target-version=3.7
py -3.7 setup.py -q bdist_wheel --skip-build

py -3.8-32 setup.py -q bdist_wininst --skip-build --target-version=3.8
py -3.8-32 setup.py -q bdist_wheel --skip-build
py -3.8 setup.py -q bdist_wininst --skip-build --target-version=3.8
Expand Down
1 change: 0 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[mypy]
show_column_numbers = true
; Target the oldest supported version in editors and default CLI
; mypy 1.5 dropped support for Python 3.7
python_version = 3.8

strict = true
Expand Down
2 changes: 1 addition & 1 deletion pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"typeCheckingMode": "basic",
// Target the oldest supported version in editors and default CLI
"pythonVersion": "3.7",
"pythonVersion": "3.8",
// Keep it simple for now by allowing both mypy and pyright to use `type: ignore`
"enableTypeIgnoreComments": true,
// Exclude from scanning when running pyright
Expand Down
2 changes: 1 addition & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
target-version = "py37" # Target the oldest supported version
target-version = "py38" # Target the oldest supported version in editors and default CLI

[lint]
select = [
Expand Down
20 changes: 5 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,14 @@
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install
from setuptools.command.install_lib import install_lib
from setuptools.modified import newer_group
from tempfile import gettempdir
from typing import Iterable

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

if sys.version_info >= (3, 8):
from setuptools.modified import newer_group
else:
from distutils.dep_util import newer_group

build_id_patch = build_id
if not "." in build_id_patch:
build_id_patch += ".0"
Expand Down Expand Up @@ -868,12 +864,6 @@ def run(self):
install.run(self)
# Custom script we run at the end of installing - this is the same script
# run by bdist_wininst
# This child process won't be able to install the system DLLs until our
# process has terminated (as distutils imports win32api!), so we must use
# some 'no wait' executor - spawn seems fine! We pass the PID of this
# process so the child will wait for us.
# XXX - hmm - a closer look at distutils shows it only uses win32api
# if _winreg fails - and this never should. Need to revisit this!
# If self.root has a value, it means we are being "installed" into
# some other directory than Python itself (eg, into a temp directory
# for bdist_wininst to use) - in which case we must *not* run our
Expand All @@ -885,7 +875,8 @@ def run(self):
if not os.path.isfile(filename):
raise RuntimeError(f"Can't find '{filename}'")
print("Executing post install script...")
# What executable to use? This one I guess.
# As of setuptools>=74.0.0, we no longer need to
# be concerned about distutils calling win32api
subprocess.Popen(
[
sys.executable,
Expand All @@ -905,8 +896,8 @@ def install(self):
# This is crazy - in setuptools 61.1.0 (and probably some earlier versions), the
# install_lib and build comments don't agree on where the .py files to install can
# be found, so we end up with a warning logged:
# `warning: my_install_lib: 'build\lib.win-amd64-3.7' does not exist -- no Python modules to install`
# (because they are actually in `build\lib.win-amd64-cpython-37`!)
# `warning: my_install_lib: 'build\lib.win-amd64-3.8' does not exist -- no Python modules to install`
# (because they are actually in `build\lib.win-amd64-cpython-38`!)
# It's not an error though, so we end up with .exe installers lacking our lib files!
builder = self.get_finalized_command("build")
if os.path.isdir(builder.build_platlib) and not os.path.isdir(self.build_dir):
Expand Down Expand Up @@ -2151,7 +2142,6 @@ def convert_optional_data_files(files):
"Intended Audience :: Developers",
"License :: OSI Approved :: Python Software Foundation License",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
Expand Down
10 changes: 1 addition & 9 deletions win32/Lib/pywin32_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
# In short, there's a directory installed by pywin32 named 'pywin32_system32'
# with some important DLLs which need to be found by Python when some pywin32
# modules are imported.
# If Python has `os.add_dll_directory()`, we need to call it with this path.
# Otherwise, we add this path to PATH.


try:
Expand All @@ -19,11 +17,5 @@
# https://docs.python.org/3/reference/import.html#__path__
for path in pywin32_system32.__path__:
if os.path.isdir(path):
if hasattr(os, "add_dll_directory"):
os.add_dll_directory(path)
# This is to ensure the pywin32 path is in the beginning to find the
# pywin32 DLLs first and prevent other PATH entries to shadow them
elif not os.environ["PATH"].startswith(path):
os.environ["PATH"] = os.environ["PATH"].replace(os.pathsep + path, "")
os.environ["PATH"] = path + os.pathsep + os.environ["PATH"]
os.add_dll_directory(path)
break
2 changes: 2 additions & 0 deletions win32/Lib/win32gui_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,8 @@ def UnpackDEV_BROADCAST(lparam):
_, _, _, x["unitmask"], x["flags"] = struct.unpack(
fmt, buf[: struct.calcsize(fmt)]
)
elif devtype == win32con.DBT_DEVTYP_PORT:
x["name"] = win32gui.PyGetString(lparam + struct.calcsize(hdr_format))
else:
raise NotImplementedError("unknown device type %d" % (devtype,))
return DEV_BROADCAST_INFO(devtype, **extra)
22 changes: 19 additions & 3 deletions win32/Lib/win32timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,27 @@ def __str__(self):
return self.displayName

def tzname(self, dt):
winInfo = self.getWinInfo(dt)
if self.dst(dt) == winInfo.daylight_bias:
"""
>>> MST = TimeZoneInfo('Mountain Standard Time')
>>> MST.tzname(datetime.datetime(2003, 8, 2))
'Mountain Daylight Time'
>>> MST.tzname(datetime.datetime(2003, 11, 25))
'Mountain Standard Time'
"""

winInfo = self.getWinInfo(dt.year)
if self.dst(dt) == -winInfo.daylight_bias:
result = self.daylightName
elif self.dst(dt) == winInfo.standard_bias:
elif self.dst(dt) == -winInfo.standard_bias:
result = self.standardName
else:
raise ValueError(
"Unexpected daylight bias",
dt,
self.dst(dt),
winInfo.daylight_bias,
winInfo.standard_bias,
)
return result

def getWinInfo(self, targetYear):
Expand Down
Loading

0 comments on commit 8a558ea

Please sign in to comment.