Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dropping support for Python 3.8 #2413

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
13 changes: 6 additions & 7 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.8", "3.9", "3.10", "3.11", "3.12", "3.13-dev"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
architecture: ["x64", "x86"]

steps:
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
- name: Run tests
# Run the tests directly from the source dir so support files (eg, .wav files etc)
# can be found - they aren't installed into the Python tree.
run: python pywin32_testall.py -v -skip-adodbapi
run: python -X dev pywin32_testall.py -v -skip-adodbapi

- name: Build wheels
run: |
Expand All @@ -76,7 +76,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13-dev"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -118,7 +118,7 @@ jobs:
- uses: actions/setup-python@v5
with:
# This job only needs to target the oldest supported version
python-version: "3.8"
python-version: "3.9"
cache: pip
cache-dependency-path: .github/workflows/main.yml
- run: pip install clang-format pycln
Expand All @@ -140,8 +140,7 @@ jobs:
strategy:
fail-fast: false
matrix:
# 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"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand All @@ -159,7 +158,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13-dev"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down
5 changes: 1 addition & 4 deletions Pythonwin/pywin/framework/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import os
import sys
import traceback
from typing import TYPE_CHECKING
from typing import Literal

import regutil
import win32api
Expand All @@ -21,9 +21,6 @@

from . import scriptutils

if TYPE_CHECKING:
from typing_extensions import Literal


# Helper for writing a Window position by name, and later loading it.
def SaveWindowSize(section, rect, state=""):
Expand Down
5 changes: 3 additions & 2 deletions Pythonwin/pywin/framework/intpyapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
import traceback
from collections.abc import Sequence

import __main__
import commctrl
Expand Down Expand Up @@ -268,7 +269,7 @@ def Activate(self):
if frame.GetWindowPlacement()[1] == win32con.SW_SHOWMINIMIZED:
frame.ShowWindow(win32con.SW_RESTORE)

def ProcessArgs(self, args, dde=None):
def ProcessArgs(self, args: Sequence[str], dde=None):
# If we are going to talk to a remote app via DDE, then
# activate it!
if (
Expand All @@ -290,7 +291,7 @@ def ProcessArgs(self, args, dde=None):
).lower()
i -= 1 # arg is /edit's parameter
par = i < len(args) and args[i] or "MISSING"
if argType in ("/nodde", "/new", "-nodde", "-new"):
if argType in ("/nodde", "/new"):
# Already handled
pass
elif argType.startswith("/goto:"):
Expand Down
4 changes: 1 addition & 3 deletions Pythonwin/pywin/idle/CallTips.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,8 @@ def get_object_at_cursor(
# How is this for a hack!
import __main__

namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(word, namespace)
return eval(word, sys.modules | __main__.__dict__)
except:
pass
return None # Can't find an object.
Expand Down
5 changes: 2 additions & 3 deletions Pythonwin/pywin/scintilla/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def list2dict(l):
# extra attributes of win32ui objects
if hasattr(ob, "_obj_"):
try:
items_dict.update(list2dict(dir(ob._obj_)))
items_dict = list2dict(dir(ob._obj_))
except AttributeError:
pass # object has no __dict__

Expand Down Expand Up @@ -650,8 +650,7 @@ def _GetObjectAtPos(self, pos=-1, bAllowCalls=0):
left, right = self._GetWordSplit(pos, bAllowCalls)
if left: # It is an attribute lookup
# How is this for a hack!
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
namespace = sys.modules | __main__.__dict__
# Get the debugger's context.
try:
from pywin.framework import interact
Expand Down
15 changes: 10 additions & 5 deletions Pythonwin/pywin/test/test_pywin.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,13 @@ def t_Browse(*args):
if __name__ != "__main__":
# make T findable by browser in __main__ namespace
setattr(__main__, __class__.__qualname__, __class__)
with mock.patch(
"pywin.mfc.dialog.GetSimpleInput", (lambda *args: __class__.__qualname__)
), mock.patch("pywin.tools.browser.Browse", t_Browse):
with (
mock.patch(
"pywin.mfc.dialog.GetSimpleInput",
(lambda *args: __class__.__qualname__),
),
mock.patch("pywin.tools.browser.Browse", t_Browse),
):
self.app.OnViewBrowse(0, 0)
hl = o.dlg.hier_list
self.assertGreater(len(hl.itemHandleMap), 10)
Expand Down Expand Up @@ -443,8 +447,9 @@ def t_brk(self):
GUIAboutToBreak()

dmod = types.ModuleType("__main__", "debugger test main")
with mock.patch("pywin.framework.scriptutils.__main__", dmod), mock.patch(
"pywin.debugger.debugger.Debugger.GUIAboutToBreak", t_brk
with (
mock.patch("pywin.framework.scriptutils.__main__", dmod),
mock.patch("pywin.debugger.debugger.Debugger.GUIAboutToBreak", t_brk),
):
mf.SendMessage(wc.WM_COMMAND, cmGo) # debh.OnGo(0, 0)
self.assertFalse(cmds_brk_next, "break commands remaining")
Expand Down
13 changes: 0 additions & 13 deletions Pythonwin/win32cmdui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,6 @@ inline void *GetPythonOleProcAddress(const char *procName)
#endif
hMod = GetModuleHandle(buf);

// XXX It is unclear why the code previously tried to identify a loaded PythonCOM DLL of
// any Python version 1.5 .. 3.9. If some InprocServer would load the DLL of a different
// Python version that would likely cause a crash. Thus deactivated.
//
// for (int i = 0; hMod == NULL && i < 40; i++) {
// #ifdef _DEBUG
// wsprintf(buf, _T("PythonCOM3%d_d.dll"), i);
// #else
// wsprintf(buf, _T("PythonCOM3%d.dll"), i);
// #endif
// hMod = GetModuleHandle(buf);
// }

if (hMod) {
void *rc = GetProcAddress(hMod, procName);
if (rc == NULL)
Expand Down
6 changes: 3 additions & 3 deletions Pythonwin/win32uimodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2559,9 +2559,9 @@ extern "C" PYW_EXPORT BOOL Win32uiApplicationInit(Win32uiHostGlue *pGlue, const
PyObject *argv = PySys_GetObject("argv");
PyInit_win32ui();
// Decide if we render sys.argv from command line.
// PY3.6- Py_Initialize sets sys.argv=NULL .
// PY3.7 Py_Initialize or intentional script triggers set sys.argv=[] .
// PY3.8+ Py_Initialize sets sys.argv=[''] - cannot be distinguished
// Python 3.6- Py_Initialize sets sys.argv=NULL .
// Python 3.7 Py_Initialize or intentional script triggers set sys.argv=[] .
// Python 3.8+ Py_Initialize sets sys.argv=[''] - cannot be distinguished
// from a pre-existing command line setup anymore. So we need to check
// another flag regarding the intended type of invokation, e.g. `cmd`
// (or untangle all that crossover startup + module + app init here)
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.8-32 setup.py -q build
@if errorlevel 1 goto failed
py -3.8 setup.py -q build
@if errorlevel 1 goto failed
py -3.9-32 setup.py -q build
@if errorlevel 1 goto failed
py -3.9 setup.py -q build
Expand Down
2 changes: 1 addition & 1 deletion build_env.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This describes how to setup the build environment for pywin32.
Double check the compiler version you need in the [Python wiki](https://wiki.python.org/moin/WindowsCompilers)
but note that Python 3.5 -> 3.13 all use version 14.X of the compiler, which,
confusingly, report themselves as V.19XX (eg, note in Python's banner,
3.5's "MSC v.1900", even 3.9b4's "MSC v.1924")
3.5's "MSC v.1900", even 3.13's "MSC v.1941")

This compiler first shipped with Visual Studio 2015, although Visual Studio
2017, 2019 and 2022 all have this compiler available, just not installed
Expand Down
1 change: 1 addition & 0 deletions com/win32com/client/gencache.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def _LoadDicts():
arc_path = loader.archive
dicts_path = os.path.join(win32com.__gen_path__, "dicts.dat")
if dicts_path.startswith(arc_path):
# Remove the leading slash as well
dicts_path = dicts_path[len(arc_path) + 1 :]
else:
# Hm. See below.
Expand Down
8 changes: 3 additions & 5 deletions isapi/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def _PatchParamsModule(params, dll_name, file_must_exist=True):
sm.Module = dll_name


def GetLoaderModuleName(mod_name, check_module=None):
def GetLoaderModuleName(mod_name: str, check_module=None):
# find the name of the DLL hosting us.
# By default, this is "_{module_base_name}.dll"
if hasattr(sys, "frozen"):
Expand All @@ -635,8 +635,7 @@ def GetLoaderModuleName(mod_name, check_module=None):
base, ext = os.path.splitext(mod_name)
path, base = os.path.split(base)
# handle the common case of 'foo.exe'/'foow.exe'
if base.endswith("w"):
base = base[:-1]
base.removesuffix("w")
# For py2exe, we have '_foo.dll' as the standard pyisapi loader - but
# 'foo.dll' is what we use (it just delegates).
# So no leading '_' on the installed name.
Expand Down Expand Up @@ -760,8 +759,7 @@ def HandleCommandLine(

# build a usage string if we don't have one.
if not parser.get_usage():
all_handlers = standard_arguments.copy()
all_handlers.update(custom_arg_handlers)
all_handlers = standard_arguments | custom_arg_handlers
parser.set_usage(build_usage(all_handlers))

# allow the user to use uninstall as a synonym for remove if it wasn't
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.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
py -3.8 setup.py -q bdist_wheel --skip-build

py -3.9-32 setup.py -q bdist_wininst --skip-build --target-version=3.9
py -3.9-32 setup.py -q bdist_wheel --skip-build
py -3.9 setup.py -q bdist_wininst --skip-build --target-version=3.9
Expand Down
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[mypy]
show_column_numbers = true
; Target the oldest supported version in editors and default CLI
python_version = 3.8
python_version = 3.9

strict = true
implicit_reexport = true
Expand Down
6 changes: 3 additions & 3 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"typeCheckingMode": "basic",
// Target the oldest supported version in editors and default CLI
"pythonVersion": "3.8",
"pythonVersion": "3.9",
// Keep it simple for now by allowing both mypy and pyright to use `type: ignore`
"enableTypeIgnoreComments": true,
// Exclude from scanning when running pyright
"exclude": [
".git/",
".git/", // Avoids scanning git branch names ending in ".py"
"build/",
// Vendored
"Pythonwin/Scintilla/",
Expand Down Expand Up @@ -38,7 +38,7 @@
"reportOptionalIterable": "warning",
"reportOptionalMemberAccess": "warning",
"reportOptionalSubscript": "warning",
// Needs fixes in types-pywin32 and requires Python 3.8 to annotate ambiguous global variables
// Needs fixes in types-pywin32 to annotate ambiguous global variables
"reportUnnecessaryComparison": "warning",
// Too many dynamically generated modules. This will require type stubs to properly fix.
"reportMissingImports": "warning",
Expand Down
28 changes: 20 additions & 8 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
target-version = "py38" # Target the oldest supported version in editors and default CLI
target-version = "py39" # Target the oldest supported version in editors and default CLI
# This file is not UTF-8
extend-exclude = ["Pythonwin/pywin/test/_dbgscript.py"]

Expand All @@ -16,12 +16,7 @@ select = [
# String formatting, concatenating, interpolation, ...
"FLY", # static-join-to-f-string
"G", # flake8-logging-format
# Note, we still want to allow multiline ISC
"UP025", # Remove unicode literals from strings
"UP030", # Use implicit references for positional format fields
# TODO: Still lots of manual fixes needed
# "UP031", # Use format specifiers instead of percent format
# "UP032", # Use f-string instead of format call
"UP", # pyupgrade

# Ensure modern type annotation syntax and best practices
# Not including those covered by type-checkers
Expand All @@ -31,21 +26,38 @@ select = [
"UP006", # non-pep585-annotation
"UP007", # non-pep604-annotation
"UP010", # unnecessary-future-import
"UP035", # deprecated-import
"UP037", # quoted-annotation

# Helps prevent circular imports and other unneeded imports
"TCH", # flake8-type-checking
]
extend-ignore = [
# We prefer explicit open mode parameter
"UP015", # redundant-open-modes
# No such concerns for stdlib
"TCH003", # typing-only-standard-library-import
# TODO: Still lots of manual fixes needed
"UP031", # Use format specifiers instead of percent format
"UP032", # Use f-string instead of format call
]

[lint.per-file-ignores]
# Explicit re-exports is fine in __init__.py, still a code smell elsewhere.
"__init__.py" = ["PLC0414"]
# TODO: Make adodbapi changes in their own PRs
"adodbapi/*" = ["C4", "YTT301", "UP031", "UP032"]
"adodbapi/*" = [
"C4",
"UP031",
"UP032",
# Dropping Python 3.8 support
"UP005",
"UP006",
"UP018",
"UP035",
"UP036",
"YTT301",
]

[lint.isort]
combine-as-imports = true
Expand Down
Loading
Loading