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 <jepler@gmail.com>
This commit is contained in:
Jeff Epler 2025-08-08 10:09:13 -05:00
parent 593ae04eeb
commit d2817bb168
12 changed files with 35 additions and 93 deletions

View file

@ -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

View file

@ -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

View file

@ -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
-----------------------------------

View file

@ -106,7 +106,7 @@ See the `ARM GCC
toolchain <https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads>`_
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

View file

@ -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

View file

@ -9,25 +9,13 @@ from __future__ import print_function
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

View file

@ -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 @@ from __future__ import print_function
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 @@ codepoint2name = {
253: "yacute", 165: "yen", 255: "yuml", 950: "zeta", 8205: "zwj", 8204: "zwnj"
}
# fmt: on
# end compatibility code
codepoint2name[ord("-")] = "hyphen"

View file

@ -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

View file

@ -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 @@ import subprocess
# "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"],

View file

@ -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 {

View file

@ -24,40 +24,20 @@
# 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 sys
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)
def hexlify_to_str(b):
return str(hexlify(b, ":"), "ascii")
# end compatibility code
import sys
import struct
import sys
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)
def hexlify_to_str(b):
return str(hexlify(b, ":"), "ascii")
sys.path.append(sys.path[0] + "/../py")
import makeqstrdata as qstrutil

View file

@ -77,7 +77,7 @@ __DFU_INTERFACE = 0
# 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