diff --git a/.github/workflows/download-arm64-libs.py b/.github/workflows/download-arm64-libs.py index 51c5bcd02..841673f98 100644 --- a/.github/workflows/download-arm64-libs.py +++ b/.github/workflows/download-arm64-libs.py @@ -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"): diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa1049f06..2af0d70a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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-dev"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13-dev"] architecture: ["x64", "x86"] steps: @@ -37,7 +37,7 @@ jobs: run: | python --version pip --version - pip install --upgrade setuptools wheel + pip install --upgrade setuptools>=74 wheel - name: Build and install run: | @@ -86,7 +86,7 @@ jobs: run: | python --version pip --version - pip install --upgrade setuptools wheel + pip install --upgrade setuptools>=74 wheel - name: Obtain ARM64 library files run: | @@ -117,7 +117,7 @@ jobs: cache-dependency-path: .github/workflows/main.yml - run: pip install clang-format pycln - run: pycln . --config=pycln.toml --check - - uses: chartboost/ruff-action@v1 + - uses: astral-sh/ruff-action@v1 with: version: "0.4.5" - uses: psf/black@stable @@ -134,7 +134,6 @@ jobs: strategy: fail-fast: false matrix: - # mypy 1.5 dropped support for Python 3.7 # mypy won't understand "3.13-dev", keeping the CI simple by just omitting it python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: @@ -154,7 +153,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13-dev"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13-dev"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/CHANGES.txt b/CHANGES.txt index 8d1012619..d8978afd5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,7 @@ 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) diff --git a/Pythonwin/pywin/test/test_pywin.py b/Pythonwin/pywin/test/test_pywin.py index 6cf973a92..49acc9d5e 100644 --- a/Pythonwin/pywin/test/test_pywin.py +++ b/Pythonwin/pywin/test/test_pywin.py @@ -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() @@ -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() diff --git a/build_all.bat b/build_all.bat index 536aee00d..ec8cbbaa2 100644 --- a/build_all.bat +++ b/build_all.bat @@ -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 diff --git a/build_env.md b/build_env.md index d9c55ac10..4f616bf99 100644 --- a/build_env.md +++ b/build_env.md @@ -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 ``` diff --git a/com/win32com/test/pippo_server.py b/com/win32com/test/pippo_server.py index c5ae2df40..d8e964623 100644 --- a/com/win32com/test/pippo_server.py +++ b/com/win32com/test/pippo_server.py @@ -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")) diff --git a/make_all.bat b/make_all.bat index c6d6a2b33..05855ddfb 100644 --- a/make_all.bat +++ b/make_all.bat @@ -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 diff --git a/mypy.ini b/mypy.ini index bd1cd4316..e4676e0c7 100644 --- a/mypy.ini +++ b/mypy.ini @@ -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 diff --git a/pyrightconfig.json b/pyrightconfig.json index 1721da1a2..d96b3fc9d 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -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 diff --git a/ruff.toml b/ruff.toml index 603043e8f..eab7fcce1 100644 --- a/ruff.toml +++ b/ruff.toml @@ -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 = [ diff --git a/setup.py b/setup.py index ce0120e24..e25465fd7 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ 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 @@ -47,11 +48,6 @@ 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" @@ -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 @@ -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, @@ -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): @@ -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", diff --git a/win32/Lib/pywin32_bootstrap.py b/win32/Lib/pywin32_bootstrap.py index fbc4f7be4..c9f59a56d 100644 --- a/win32/Lib/pywin32_bootstrap.py +++ b/win32/Lib/pywin32_bootstrap.py @@ -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: @@ -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 diff --git a/win32/src/_win32sysloader.cpp b/win32/src/_win32sysloader.cpp index 0a9e4dcac..9a9ef7c27 100644 --- a/win32/src/_win32sysloader.cpp +++ b/win32/src/_win32sysloader.cpp @@ -52,13 +52,7 @@ static PyObject *PyLoadModule(PyObject *self, PyObject *args) if (!modName) return NULL; - // Python 3.7 vs 3.8 use different flags for LoadLibraryEx and we match them. - // See github issue 1787. -#if (PY_VERSION_HEX < 0x03080000) - HINSTANCE hinst = LoadLibraryEx(modName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); -#else HINSTANCE hinst = LoadLibraryEx(modName, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); -#endif PyMem_Free(modName); if (hinst == NULL) { Py_INCREF(Py_None);