Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/install-vs-components.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
text=True,
shell=True,
).strip()
components_to_add = (
["Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64"]
components_to_add = [
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64"
if platform.machine() == "ARM64"
else ["Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL"]
)
else "Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL"
]
args = (
"vs_installer.exe",
"modify",
Expand Down
31 changes: 20 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ concurrency:
jobs:
test:
name: Build and test
runs-on: windows-2022
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
architecture: [x64, x86]
python-architecture: [x64, x86, arm64]
include:
- os: windows-2022
- python-architecture: arm64
os: windows-11-arm
Comment on lines +25 to +28
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is saying "add os as a matrix element that defaults to windows-2022" and "when python-architecture == arm64, use windows-11-arm instead"

exclude:
# actions/setup-python does not provide prebuilt arm64 Python before 3.11
- python-architecture: arm64
python-version: "3.9"
- python-architecture: arm64
python-version: "3.10"
env:
# TODO: We can't yet run tests with PYTHONDEVMODE=1, let's emulated it as much as we can
# https://docs.python.org/3/library/devmode.html#effects-of-the-python-development-mode
Expand All @@ -37,7 +47,7 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}
architecture: ${{ matrix.python-architecture }}
cache: pip
cache-dependency-path: .github/workflows/main.yml
check-latest: true
Expand Down Expand Up @@ -83,21 +93,20 @@ jobs:
# Upload artifacts even if tests fail
if: ${{ always() }}
with:
name: artifacts-${{ matrix.python-version }}-${{ matrix.architecture }}
name: artifacts-${{ matrix.python-version }}-${{ matrix.python-architecture }}
path: dist/*.whl
if-no-files-found: error

# We cannot build and test on ARM64, so we cross-compile.
# Later, when available, we can add tests using this wheel on ARM64 VMs
build_arm64:
name: Cross-compile ARM
# actions/setup-python does not provide prebuilt arm64 Python before 3.11, so we cross-compile.
cross_compile_arm64:
name: Cross-compile ARM64
runs-on: windows-2022
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# pythonarm64 NuGet's has no download for Python ~=3.9.11
python-version: ["3.9.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
# pythonarm64 NuGet has no download for Python 3.8 and Python ~=3.9.11
python-version: ["3.9.10", "3.10"]
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -128,7 +137,7 @@ jobs:

merge:
runs-on: ubuntu-latest
needs: [test, build_arm64]
needs: [test, cross_compile_arm64]
steps:
- name: Merge Artifacts
uses: actions/upload-artifact/merge@v4
Expand Down
12 changes: 11 additions & 1 deletion com/win32com/test/testArrays.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Originally contributed by Stefan Schukat as part of this arbitrary-sized
# arrays patch.
from __future__ import annotations

import platform
import unittest

from win32com.client import gencache
from win32com.test import util

ZeroD = 0
OneDEmpty = []
OneDEmpty: list[int] = []
OneD = [1, 2, 3]
TwoD = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]

Expand Down Expand Up @@ -49,6 +53,12 @@ def _normalize_array(a):
return ret


@unittest.skipIf(
platform.machine() == "ARM64",
"PyCOMTest.ArrayTest cannot currently be run on ARM64 "
+ "due to lacking win32com.universal implementation "
+ "in com/win32com/src/univgw.cpp",
)
class ArrayTest(util.TestCase):
def setUp(self):
self.arr = gencache.EnsureDispatch("PyCOMTest.ArrayTest", bForDemand=False)
Expand Down
41 changes: 21 additions & 20 deletions com/win32com/test/testPyComTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import win32timezone
import winerror
from win32api import CloseHandle, GetCurrentProcessId, OpenProcess
from win32com import universal
from win32com.client import (
VARIANT,
CastTo,
Expand All @@ -25,6 +24,7 @@
gencache,
register_record_class,
)
from win32com.universal import RegisterInterfaces
from win32process import GetProcessMemoryInfo

# This test uses a Python implemented COM server - ensure correctly registered.
Expand All @@ -46,10 +46,6 @@
print(f"The PyCOMTest module can not be located or generated.\n{importMsg}\n")
raise RuntimeError(importMsg) from error

# We had a bg where RegisterInterfaces would fail if gencache had
# already been run - exercise that here
universal.RegisterInterfaces("{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1)

verbose = 0


Expand Down Expand Up @@ -175,7 +171,7 @@ def _DumpFireds(self):
if not self.fireds:
print("ERROR: Nothing was received!")
for firedId, no in self.fireds.items():
progress("ID %d fired %d times" % (firedId, no))
progress(f"ID {firedId} fired {no} times")


# Test everything which can be tested using both the "dynamic" and "generated"
Expand Down Expand Up @@ -891,34 +887,39 @@ def TestVTableMI():
pass


def TestQueryInterface(long_lived_server=0, iterations=5):
def TestQueryInterface(long_lived_server=False, iterations=5):
tester = win32com.client.Dispatch("PyCOMTest.PyCOMTest")
if long_lived_server:
# Create a local server
t0 = win32com.client.Dispatch(
"Python.Test.PyCOMTest", clsctx=pythoncom.CLSCTX_LOCAL_SERVER
)
# Request custom interfaces a number of times
prompt = [
"Testing QueryInterface without long-lived local-server #%d of %d...",
"Testing QueryInterface with long-lived local-server #%d of %d...",
]

# Request custom interfaces a number of time
for i in range(iterations):
progress(prompt[long_lived_server != 0] % (i + 1, iterations))
progress(
f"Testing QueryInterface "
+ ("with" if long_lived_server else "without")
+ f" long-lived local-server #{i + 1} of {iterations}..."
)
tester.TestQueryInterface()


class Tester(win32com.test.util.TestCase):
def testVTableInProc(self):
def testRegisterInterfacesAfterGencache(self) -> None:
# We had a bug where RegisterInterfaces would fail if gencache had
# already been run - exercise that here
RegisterInterfaces("{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1)

def testVTableInProc(self) -> None:
# We used to crash running this the second time - do it a few times
for i in range(3):
progress("Testing VTables in-process #%d..." % (i + 1))
progress(f"Testing VTables in-process #{(i + 1)}...")
TestVTable(pythoncom.CLSCTX_INPROC_SERVER)

def testVTableLocalServer(self):
def testVTableLocalServer(self) -> None:
for i in range(3):
progress("Testing VTables out-of-process #%d..." % (i + 1))
progress(f"Testing VTables out-of-process #{(i + 1)}...")
TestVTable(pythoncom.CLSCTX_LOCAL_SERVER)

def testVTable2(self):
Expand All @@ -930,15 +931,15 @@ def testVTableMI(self):
TestVTableMI()

def testMultiQueryInterface(self):
TestQueryInterface(0, 6)
TestQueryInterface(False, 6)
# When we use the custom interface in the presence of a long-lived
# local server, i.e. a local server that is already running when
# we request an instance of our COM object, and remains afterwards,
# then after repeated requests to create an instance of our object
# the custom interface disappears -- i.e. QueryInterface fails with
# E_NOINTERFACE. Set the upper range of the following test to 2 to
# pass this test, i.e. TestQueryInterface(1,2)
TestQueryInterface(1, 6)
# pass this test, i.e. TestQueryInterface(True, 2)
TestQueryInterface(True, 6)

def testDynamic(self):
TestDynamic()
Expand Down
17 changes: 11 additions & 6 deletions com/win32com/test/testall.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import getopt
import os
import platform
import re
import sys
import traceback
Expand All @@ -22,7 +23,6 @@
win32com.__path__[0] = win32com_src_dir

import pythoncom
import win32com.client
from pywin32_testutil import TestLoader, TestRunner
from win32com.test.util import (
CapturingFunctionTestCase,
Expand Down Expand Up @@ -52,7 +52,7 @@ def CleanGenerated():

if os.path.isdir(win32com.__gen_path__):
if verbosity > 1:
print("Deleting files from %s" % (win32com.__gen_path__))
print(f"Deleting files from", win32com.__gen_path__)
shutil.rmtree(win32com.__gen_path__)
import win32com.client.gencache

Expand All @@ -78,11 +78,17 @@ def ExecuteSilentlyIfOK(cmd, testcase):
rc = f.close()
if rc:
print(data)
testcase.fail("Executing '%s' failed (%d)" % (cmd, rc))
testcase.fail(f"Executing '{cmd}' failed ({rc})")
# for "_d" builds, strip the '[xxx refs]' line
return RemoveRefCountOutput(data)


@unittest.skipIf(
platform.machine() == "ARM64",
"PyCOMTest cannot currently be run on ARM64 "
+ "due to lacking win32com.universal implementation "
+ "in com/win32com/src/univgw.cpp",
)
class PyCOMTest(TestCase):
no_leak_tests = True # done by the test itself

Expand Down Expand Up @@ -291,8 +297,7 @@ def usage(why):
print("These tests may take *many* minutes to run - be patient!")
print("(running from python.exe will avoid these leak tests)")
print(
"Executing level %d tests - %d test cases will be run"
% (testLevel, suite.countTestCases())
f"Executing level {testLevel} tests - {suite.countTestCases()} test cases will be run"
)
if verbosity == 1 and suite.countTestCases() < 70:
# A little row of markers so the dots show how close to finished
Expand All @@ -307,7 +312,7 @@ def usage(why):
desc = "\n".join(traceback.format_exception_only(exc_type, exc_val))
testResult.stream.write(f"{mod_name}: {desc}")
testResult.stream.writeln(
"*** %d test(s) could not be run ***" % len(import_failures)
f"*** {len(import_failures)} test(s) could not be run ***"
)

# re-print unit-test error here so it is noticed
Expand Down
2 changes: 1 addition & 1 deletion make_all.bat
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ py -3.13 -m build --wheel
py -3.14-32 -m build --wheel
py -3.14 -m build --wheel

rem Check /build_env.md#build-environment to make sure you have all the required ARM64 components installed
rem Check /build_env.md#cross-compiling-for-arm64-microsoft-visual-c-141-and-up to make sure you have all the required ARM64 components installed
py -3.10 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64"
py -3.11 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64"
py -3.12 -m build --wheel --config-setting=--build-option="build_ext --plat-name=win-arm64 build --plat-name=win-arm64 bdist_wheel --plat-name=win-arm64"
Expand Down
Loading