Xqt has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1361161?usp=email )

Change subject: cleanup: Drop Python 3.9 code and update documentation
......................................................................

cleanup: Drop Python 3.9 code and update documentation

Bug: T423572
Change-Id: I78f7b48e486973d3c273d2874e84c4a3a14fd171
---
M .github/workflows/pywikibot-ci.yml
M dev-requirements.txt
M docs/compatibility.rst
M docs/index.rst
M pywikibot/README.rst
M pywikibot/__init__.py
M requirements.txt
M scripts/pyproject.toml
M setup.py
M tests/README.rst
M tests/login_tests.py
M tests/pwb_tests.py
M tests/tools_tests.py
M tests/utils.py
14 files changed, 17 insertions(+), 51 deletions(-)

Approvals:
  Xqt: Verified; Looks good to me, approved
  Mahveotm: Looks good to me, approved




diff --git a/.github/workflows/pywikibot-ci.yml 
b/.github/workflows/pywikibot-ci.yml
index 2eec49f..36eac7a 100644
--- a/.github/workflows/pywikibot-ci.yml
+++ b/.github/workflows/pywikibot-ci.yml
@@ -139,10 +139,8 @@
           NO_PROXY: ""
         run: |
           python pwb.py version
-          if [ "${{ matrix.site }}" != "wikisource:zh" ] ||
-             [ "${{ matrix.python-version }}" = "pypy3.9" ] ||
-             [ "${{ matrix.python-version }}" = "3.9" ]; then
-            coverage run -m unittest discover -vv -p \"*_tests.py\";
+          if [ "${{ matrix.site }}" != "wikisource:zh" ]; then
+            coverage run -m unittest discover -vv -p "*_tests.py"
           else
             coverage run -m pytest
           fi
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 2f034cf..cc4c6fa 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,5 @@
 # This is a requirements file for development dependencies.
 #
-# Python 3.10 is required for pytest and pre-commit
 pytest >= 9.2.0
 pytest-attrib >= 0.1.3
 pytest-xvfb >= 3.1.1
diff --git a/docs/compatibility.rst b/docs/compatibility.rst
index a54fb6e..384c5fa 100644
--- a/docs/compatibility.rst
+++ b/docs/compatibility.rst
@@ -13,7 +13,8 @@
 =================  =====================  ======  ====  =======  
=================  ================
 Pywikibot version  Python version         No GIL  PyPy  GraalPy  MediaWiki 
version  1st Release Date
 =================  =====================  ======  ====  =======  
=================  ================
-11.0โ€“|version|     3.9โ€“3.15               ๐Ÿงช      ๐Ÿ›ก๏ธ    โš ๏ธ       1.31โ€“1.46      
    2026-02-09
+12.0โ€“|version|     3.10โ€“3.16              ๐Ÿงช      ๐Ÿ›ก๏ธ    ๐Ÿงช๏ธ       1.33โ€“1.48      
    2026/2027
+11.0โ€“11.10.0       3.9โ€“3.15               ๐Ÿงช      ๐Ÿ›ก๏ธ    โš ๏ธ       1.31โ€“1.46      
    2026-02-09
 10.0โ€“10.7.4        3.8โ€“3.14               โ“      โœ…    โŒ       1.31โ€“1.44        
  2025-03-01
 9.0โ€“9.6.3          3.7โ€“3.13               โ“      โœ…    โŒ       1.27โ€“1.42        
  2024-03-08
 8.0โ€“8.6            3.6.1โ€“3.12             โŒ      โœ…    โŒ       1.27โ€“1.41        
  2023-01-21
diff --git a/docs/index.rst b/docs/index.rst
index 2ea12d0..b78ca3b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,8 +21,7 @@
 whether you have Python installed and to find its version, just type
 ``python`` at the CMD or shell prompt.

-Python 3.9 or higher is currently required to run the bot but Python 3.10 or
-higher is strictly recommended.
+Python 3.10 or higher is currently required to run the bot.

 Pywikibot and this documentation are licensed under the
 :ref:`MIT license`;
diff --git a/pywikibot/README.rst b/pywikibot/README.rst
index 0ea442e..b3ac5d7 100644
--- a/pywikibot/README.rst
+++ b/pywikibot/README.rst
@@ -20,7 +20,7 @@
   * googlesearch-python to access Google Web API for use with pagegenerators.py


-You need to have at least Python version `3.9 
<https://www.python.org/downloads/>`_
+You need to have at least Python version `3.10 
<https://www.python.org/downloads/>`_
 or newer installed on your computer to be able to run any of the code in this
 package. Please refer the manual at mediawiki for further details and
 restrictions.
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index a738738..67f083a 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -77,7 +77,7 @@
 else:
     class ShutDown(Exception):

-        """Dummy exception for Python 3.9-3.12."""
+        """Dummy exception for Python 3.10-3.12."""


 __all__ = (
diff --git a/requirements.txt b/requirements.txt
index 1fab7e6..43efee5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -43,9 +43,9 @@
 python-stdnum >= 2.2

 # GUI
-# Pillow cannot be installed with GraalPy, Python 3.9 or PyPy < 3.11
+# Pillow cannot be installed with GraalPy or PyPy < 3.11
 Pillow>=12.2.0; platform_python_implementation == "PyPy" and python_version >= 
"3.11"
-Pillow>=12.2.0; platform_python_implementation == "CPython" and  
python_version >= "3.10"
+Pillow>=12.2.0; platform_python_implementation == "CPython"

 # core pagegenerators
 googlesearch-python >= 1.3.0
diff --git a/scripts/pyproject.toml b/scripts/pyproject.toml
index f494477..18e6446 100644
--- a/scripts/pyproject.toml
+++ b/scripts/pyproject.toml
@@ -101,13 +101,13 @@
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3 :: Only",
-    "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
     "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: 3.14",
     "Programming Language :: Python :: 3.15",
+    "Programming Language :: Python :: 3.16",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
     "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Wiki",
diff --git a/setup.py b/setup.py
index aa2f694..5e5a0a0 100755
--- a/setup.py
+++ b/setup.py
@@ -45,12 +45,11 @@
     'memento': ['memento_client==0.6.1'],
     'wikitextparser': ['wikitextparser>=0.56.4'],
     'mysql': ['PyMySQL >= 1.1.2'],
-    # Pillow cannot be installed with GraalPy, Python 3.9 or PyPy < 3.11
+    # Pillow cannot be installed with GraalPy or PyPy < 3.11
     'Tkinter': [
         'Pillow>=12.2.0; platform_python_implementation == "PyPy" '
         'and python_version >= "3.11"',
-        'Pillow>=12.2.0; platform_python_implementation == "CPython" '
-        'and  python_version >= "3.10"',
+        'Pillow>=12.2.0; platform_python_implementation == "CPython"',
     ],
     'mwoauth': [
         'PyJWT != 2.10.0, != 2.10.1',  # T380270
diff --git a/tests/README.rst b/tests/README.rst
index 3875a61..ab0a25b 100644
--- a/tests/README.rst
+++ b/tests/README.rst
@@ -30,8 +30,6 @@

 **pytest**

-.. note:: Running the test suite with pytest requires Python 3.10 or higher.
-
 ::

     pip install "pytest >= 9.0.3"
diff --git a/tests/login_tests.py b/tests/login_tests.py
index 73b776f..77554b7 100755
--- a/tests/login_tests.py
+++ b/tests/login_tests.py
@@ -13,7 +13,6 @@
 import builtins
 import datetime
 import unittest
-import uuid
 from collections import defaultdict
 from io import StringIO
 from pathlib import Path
@@ -235,23 +234,12 @@

     def test_eval_security(self) -> None:
         """Test security that password file does not use eval() function."""
-        # File-based checks are limited to Python 3.9 only.
-        # On newer versions, self.stat patching in setUp() fails,
-        # making the file appear to exist.
-        use_file = PYTHON_VERSION[:2] == (3, 9)
-
+        # Note: File-based checks is not supportrd with Python 3.10+.
         builtins.exploit_value = False
         exploit_code = (
             "__import__('builtins').__dict__"
             ".__setitem__('exploit_value', True)"
         )
-        if use_file:
-            exploit_filename = f'pwb_rce_{uuid.uuid4().hex[:8]}.txt'
-            exploit_file = Path(exploit_filename)
-            exploit_code = (
-                f"__import__('pathlib').Path('{exploit_filename}').touch() or "
-                + exploit_code
-            )

         with self.subTest(test='Test ValueError'), \
              self.assertRaisesRegex(ValueError,
@@ -263,13 +251,6 @@
         with self.subTest(test='Test value was modified'):
             self.assertFalse(exploit_value)  # noqa: F821

-        if use_file:
-            with self.subTest(test='Test file exists'):
-                self.assertFalse(exploit_file.exists())
-
-            # cleanup file (should never happen)
-            exploit_file.unlink(missing_ok=True)
-

 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/pwb_tests.py b/tests/pwb_tests.py
index d279a45..09b4e7f 100755
--- a/tests/pwb_tests.py
+++ b/tests/pwb_tests.py
@@ -18,6 +18,7 @@
 import pywikibot
 from pywikibot import config
 from pywikibot.scripts import wrapper
+from pywikibot.tools import PYTHON_VERSION
 from tests import create_path_func, join_tests_path
 from tests.aspects import PwbTestCase
 from tests.utils import execute, execute_pwb
@@ -160,15 +161,12 @@
         :func:`pywikibot.scripts.wrapper.run` (T420109).
         """
         eps = entry_points()
-        try:
-            console = eps.select(group='console_scripts')
-        except AttributeError:  # Python < 3.10 compatibility path
-            console = eps.get('console_scripts', [])  # type: ignore[arg-type]
+        console = eps.select(group='console_scripts')

         pwb_eps = [ep for ep in console if ep.name == 'pwb']

         if not pwb_eps:
-            if sys.version_info >= (3, 11):
+            if PYTHON_VERSION >= (3, 11):
                 import tomllib
             else:
                 try:
diff --git a/tests/tools_tests.py b/tests/tools_tests.py
index 32f719c..ec4d46b 100755
--- a/tests/tools_tests.py
+++ b/tests/tools_tests.py
@@ -20,7 +20,6 @@

 from pywikibot import config, tools
 from pywikibot.tools import (
-    PYTHON_VERSION,
     SevenZipFile,
     cached,
     classproperty,
@@ -957,17 +956,13 @@
             'a.b.c.d',
             '192.168.0.1\n',
             '256.0.0.1',
+            '01.02.03.04'
         )

         for address in invalid_addresses:
             with self.subTest(ip_address=address):
                 self.assertFalse(is_ip_address(address))

-        address = '01.02.03.04'
-        with self.subTest(ip_address=address):
-            self.assertEqual(is_ip_address(address),
-                             PYTHON_VERSION < (3, 9, 5))
-
     def test_valid_ipv6_addresses(self) -> None:
         """Check with valid IPv6 addresses."""
         valid_addresses = (
diff --git a/tests/utils.py b/tests/utils.py
index 532bba9..09bf200 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -665,8 +665,6 @@
         ):
             page.save()

-    .. note:: The last sample uses Python 3.10 syntax.
-
     .. version-added:: 6.2
     .. version-changed:: 9.3
        *code* parameter was added

-- 
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1361161?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I78f7b48e486973d3c273d2874e84c4a3a14fd171
Gerrit-Change-Number: 1361161
Gerrit-PatchSet: 5
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Mahveotm <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to