From d2817bb1683dfc301d8961ec0251507aa9d29d18 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Aug 2025 10:09:13 -0500 Subject: [PATCH] general: Remove Python 2.7 support. Python 2.7 has been EOL since January 2020. Ubuntu oldoldlts (Focal Fossa, 20.04) has Python 3.8. Debian oldoldstable (Buster, from 2019) has Python 3.7. RHEL 8 (from 2019) has Python 3.6. It's easier than ever to install a modern Python using uv. Given this, it seems like a fine idea to drop Python 2.7 support. Even though the build is not tested on Python as old as 3.3, I left comments stating that "3.3+" is the baseline Python version. However, it might make sense to bump this to e.g., 3.10, the oldest Python 3 version used during CI. Or, using uv or another method actually test on the oldest Python interpreter that is desirable to support (uv goes back to Python 3.7 easily; in October 2025, the oldest supported Python interpreter version will be 3.10) Signed-off-by: Jeff Epler --- .github/workflows/mpy_format.yml | 2 +- .github/workflows/ports_unix.yml | 4 ++-- README.md | 5 ++--- docs/develop/gettingstarted.rst | 2 +- ports/stm32/boards/pllvalues.py | 5 ++--- ports/stm32/make-stmconst.py | 24 +++++--------------- py/makeqstrdata.py | 11 ++------- py/makeqstrdefs.py | 2 +- py/makeversionhdr.py | 14 +----------- tools/ci.sh | 11 +++------ tools/mpy-tool.py | 38 ++++++++------------------------ tools/pydfu.py | 2 +- 12 files changed, 31 insertions(+), 89 deletions(-) diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml index b6768a46c3cdc..a8b102c71204a 100644 --- a/.github/workflows/mpy_format.yml +++ b/.github/workflows/mpy_format.yml @@ -15,7 +15,7 @@ concurrency: jobs: test: - runs-on: ubuntu-22.04 # use 22.04 to get python2 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index f3f613a789af3..b4d2b181bcd53 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -121,7 +121,7 @@ jobs: run: tests/run-tests.py --print-failures nanbox: - runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386 + runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - uses: actions/checkout@v4 - name: Install packages @@ -135,7 +135,7 @@ jobs: run: tests/run-tests.py --print-failures longlong: - runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386 + runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386 steps: - uses: actions/checkout@v4 - name: Install packages diff --git a/README.md b/README.md index c78a2384604fe..9025da64444c0 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,8 @@ This repository contains the following components: - [examples/](examples/) -- a few example Python scripts. "make" is used to build the components, or "gmake" on BSD-based systems. -You will also need bash, gcc, and Python 3.3+ available as the command `python3` -(if your system only has Python 2.7 then invoke make with the additional option -`PYTHON=python2`). Some ports (rp2 and esp32) additionally use CMake. +You will also need bash, gcc, and Python 3.3+ available as the command `python3`. +Some ports (rp2 and esp32) additionally use CMake. Supported platforms & architectures ----------------------------------- diff --git a/docs/develop/gettingstarted.rst b/docs/develop/gettingstarted.rst index fed632ea1ac15..329d218a8799e 100644 --- a/docs/develop/gettingstarted.rst +++ b/docs/develop/gettingstarted.rst @@ -106,7 +106,7 @@ See the `ARM GCC toolchain `_ for the latest details. -Python is also required. Python 2 is supported for now, but we recommend using Python 3. +Python 3 is also required. Check that you have Python available on your system: .. code-block:: bash diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index ae042d999cec8..987b784a60884 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -105,7 +105,7 @@ def compute_pll2(hse, sys, relax_pll48): # VCO_OUT must be between 192MHz and 432MHz if sys * P not in mcu.range_vco_out: continue - NbyM = float(sys * P) / hse # float for Python 2 + NbyM = sys * P / hse # scan M M_min = mcu.range_n[0] // int(round(NbyM)) # starting value while mcu.range_vco_in[-1] * M_min < hse: @@ -121,7 +121,7 @@ def compute_pll2(hse, sys, relax_pll48): # N is restricted if N not in mcu.range_n: continue - Q = float(sys * P) / 48 # float for Python 2 + Q = sys * P / 48 # Q must be an integer in a set range if close_int(Q) and round(Q) in mcu.range_q: # found valid values @@ -142,7 +142,6 @@ def compute_pll2(hse, sys, relax_pll48): def compute_derived(hse, pll): - hse = float(hse) # float for Python 2 M, N, P, Q = pll vco_in = hse / M vco_out = hse * N / M diff --git a/ports/stm32/make-stmconst.py b/ports/stm32/make-stmconst.py index 4ef6143bda560..ff84977ad0268 100644 --- a/ports/stm32/make-stmconst.py +++ b/ports/stm32/make-stmconst.py @@ -9,25 +9,13 @@ import argparse import re -# Python 2/3 compatibility -import platform -if platform.python_version_tuple()[0] == "2": - - def convert_bytes_to_str(b): - return b - -elif platform.python_version_tuple()[0] == "3": - - def convert_bytes_to_str(b): - try: - return str(b, "utf8") - except ValueError: - # some files have invalid utf8 bytes, so filter them out - return "".join(chr(l) for l in b if l <= 126) - - -# end compatibility code +def convert_bytes_to_str(b): + try: + return str(b, "utf8") + except ValueError: + # some files have invalid utf8 bytes, so filter them out + return "".join(chr(l) for l in b if l <= 126) # given a list of (name,regex) pairs, find the first one that matches the given line diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 3a9c7aff52536..8cd9bdc704aa8 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -1,7 +1,7 @@ """ Process raw qstr file and output qstr data with length, hash and data bytes. -This script works with Python 2.6, 2.7, 3.3 and 3.4. +This script works with Python 3.3+. """ from __future__ import print_function @@ -9,13 +9,7 @@ import re import sys -# Python 2/3/MicroPython compatibility: -# - iterating through bytes is different -# - codepoint2name from html.entities is hard-coded -if sys.version_info[0] == 2: - bytes_cons = lambda val, enc=None: bytearray(val) -elif sys.version_info[0] == 3: # Also handles MicroPython - bytes_cons = bytes +bytes_cons = bytes # fmt: off codepoint2name = { @@ -57,7 +51,6 @@ 253: "yacute", 165: "yen", 255: "yuml", 950: "zeta", 8205: "zwj", 8204: "zwnj" } # fmt: on -# end compatibility code codepoint2name[ord("-")] = "hyphen" diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index 8e930ef50616f..3f53e1ff31d81 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -2,7 +2,7 @@ This script processes the output from the C preprocessor and extracts all qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. -This script works with Python 2.6, 2.7, 3.3 and 3.4. +This script works with Python 3.3+. """ from __future__ import print_function diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 406a061a0948a..b9ea72f896cb0 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -1,7 +1,7 @@ """ Generate header file with macros defining MicroPython version info. -This script works with Python 2.6, 2.7, 3.3 and 3.4. +This script works with Python 3.3+. """ from __future__ import print_function @@ -22,12 +22,6 @@ # "vX.Y.Z-preview.N.gHASH.dirty" -- building at any subsequent commit in the cycle # with local changes def get_version_info_from_git(repo_path): - # Python 2.6 doesn't have check_output, so check for that - try: - subprocess.check_output - except AttributeError: - return None - # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( @@ -48,12 +42,6 @@ def get_version_info_from_git(repo_path): def get_hash_from_git(repo_path): - # Python 2.6 doesn't have check_output, so check for that. - try: - subprocess.check_output - except AttributeError: - return None - try: return subprocess.check_output( ["git", "rev-parse", "--short", "HEAD"], diff --git a/tools/ci.sh b/tools/ci.sh index 8f045639b80c9..b12be02f4aa1a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -127,15 +127,12 @@ function ci_code_size_build { function ci_mpy_format_setup { sudo apt-get update - sudo apt-get install python2.7 sudo pip3 install pyelftools - python2.7 --version python3 --version } function ci_mpy_format_test { # Test mpy-tool.py dump feature on bytecode - python2.7 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy # Build MicroPython @@ -666,12 +663,11 @@ function ci_unix_coverage_run_native_mpy_tests { function ci_unix_32bit_setup { sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 python2.7 + sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 sudo pip3 install setuptools sudo pip3 install pyelftools sudo pip3 install ar gcc --version - python2.7 --version python3 --version } @@ -689,13 +685,12 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { } function ci_unix_nanbox_build { - # Use Python 2 to check that it can run the build scripts - ci_unix_build_helper PYTHON=python2.7 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" + ci_unix_build_helper VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_nanbox_run_tests { - ci_unix_run_tests_full_no_native_helper nanbox PYTHON=python2.7 + ci_unix_run_tests_full_no_native_helper nanbox } function ci_unix_longlong_build { diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 69d3a6c06f6b3..99ea86e369f9e 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -24,41 +24,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# Python 2/3/MicroPython compatibility code -from __future__ import print_function +import struct import sys +from binascii import hexlify -if sys.version_info[0] == 2: - from binascii import hexlify as hexlify_py2 - - str_cons = lambda val, enc=None: str(val) - bytes_cons = lambda val, enc=None: bytearray(val) - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: type(o) is bytearray - is_int_type = lambda o: isinstance(o, int) or isinstance(o, long) # noqa: F821 - - def hexlify_to_str(b): - x = hexlify_py2(b) - return ":".join(x[i : i + 2] for i in range(0, len(x), 2)) - -elif sys.version_info[0] == 3: # Also handles MicroPython - from binascii import hexlify +str_cons = str +bytes_cons = bytes +is_str_type = lambda o: isinstance(o, str) +is_bytes_type = lambda o: isinstance(o, bytes) +is_int_type = lambda o: isinstance(o, int) - str_cons = str - bytes_cons = bytes - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: isinstance(o, bytes) - is_int_type = lambda o: isinstance(o, int) - def hexlify_to_str(b): - return str(hexlify(b, ":"), "ascii") +def hexlify_to_str(b): + return str(hexlify(b, ":"), "ascii") -# end compatibility code - -import sys -import struct - sys.path.append(sys.path[0] + "/../py") import makeqstrdata as qstrutil diff --git a/tools/pydfu.py b/tools/pydfu.py index cd7354818cdea..280de0ccde9bb 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -77,7 +77,7 @@ # Python 3 deprecated getargspec in favour of getfullargspec, but # Python 2 doesn't have the latter, so detect which one to use -getargspec = getattr(inspect, "getfullargspec", getattr(inspect, "getargspec", None)) +getargspec = inspect.getfullargspec if "length" in getargspec(usb.util.get_string).args: # PyUSB 1.0.0.b1 has the length argument