diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5051645f..c7ff87e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "ubuntu-24.04-arm", "macos-latest", "windows-latest"] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.9", "pypy3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.9", "pypy3.10"] exclude: - os: "macos-latest" python-version: "pypy3.10" @@ -118,7 +118,7 @@ jobs: run: nox -vs integration -p ${{ matrix.python-version }} -- -m "not require_secrets" - name: Run integration tests (with secrets) # Limit CI workload by running integration tests with secrets only on edge Python versions. - if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' && contains(fromJSON('["3.8", "pypy3.10", "3.13"]'), matrix.python-version) }} + if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' && contains(fromJSON('["3.8", "pypy3.10", "3.14"]'), matrix.python-version) }} run: nox -vs integration -p ${{ matrix.python-version }} -- -m "require_secrets" --cleanup test-docker: timeout-minutes: 90 diff --git a/changelog.d/+python-3.14-support.infrastructure.md b/changelog.d/+python-3.14-support.infrastructure.md new file mode 100644 index 00000000..e803e754 --- /dev/null +++ b/changelog.d/+python-3.14-support.infrastructure.md @@ -0,0 +1 @@ +Added Python 3.14 support to CI/CD pipeline and test matrix. \ No newline at end of file diff --git a/changelog.d/1119.fixed.md b/changelog.d/1119.fixed.md new file mode 100644 index 00000000..e3eb9cfb --- /dev/null +++ b/changelog.d/1119.fixed.md @@ -0,0 +1 @@ +Fixed SystemError buffer overflow crash on Python 3.14+ caused by rst2ansi's terminal size detection bug. The CLI now gracefully handles this error and continues to function normally. \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index 995f6fb5..c6b925ce 100644 --- a/noxfile.py +++ b/noxfile.py @@ -42,6 +42,7 @@ '3.11', '3.12', '3.13', + '3.14', ] if NOX_PYTHONS is None else NOX_PYTHONS.split(',') diff --git a/pyproject.toml b/pyproject.toml index e1c83b0b..d99577eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ "argcomplete>=3.5.2,<4", diff --git a/test/integration/test_help.py b/test/integration/test_help.py index 96c289f7..8835ca50 100644 --- a/test/integration/test_help.py +++ b/test/integration/test_help.py @@ -7,10 +7,18 @@ # License https://www.backblaze.com/using_b2_code.html # ###################################################################### +import os import platform import re import subprocess +import pytest + +skip_on_windows = pytest.mark.skipif( + platform.system() == 'Windows', + reason='PTY tests require Unix-like system', +) + def test_help(cli_version): p = subprocess.run( @@ -26,3 +34,62 @@ def test_help(cli_version): expected_name += '.exe' assert re.match(r'^_?b2(v\d+)?(\.exe)?$', expected_name) # test sanity check assert f'{expected_name} --help' in p.stdout + + +@skip_on_windows +def test_help_with_tty(cli_version): + """ + Test that B2 CLI works correctly with a real TTY (pseudo-terminal). + + This test specifically verifies that the rst2ansi buffer overflow bug + on Python 3.14+ is properly handled. The bug occurs when rst2ansi's + get_terminal_size() function passes a 4-byte buffer to TIOCGWINSZ ioctl + which expects 8 bytes. + + See: https://github.com/Backblaze/B2_Command_Line_Tool/issues/1119 + + NOTE: This test uses pexpect to spawn a subprocess with a real PTY. + It works correctly in CI even with pytest-xdist parallelization. + However, when run locally with nox, the test environment may not properly + trigger the buffer overflow, causing the test to pass even without the fix. + This is due to differences in how PTY state is managed in local vs CI environments. + """ + pexpect = pytest.importorskip('pexpect') + + # Set up environment - remove LINES/COLUMNS to ensure ioctl is called + env = os.environ.copy() + env.pop('LINES', None) + env.pop('COLUMNS', None) + + # Spawn b2 --help with pexpect to create a real PTY + # This is where the bug would trigger on Python 3.14 without our fix + child = pexpect.spawn( + cli_version, + ['--help'], + env=env, + timeout=10, + ) + + # Wait for process to complete + child.expect(pexpect.EOF) + + # Get the output + output = child.before.decode('utf-8', errors='replace') + + # Check exit status + child.close() + exit_code = child.exitstatus + + # Verify the command succeeded and produced help output + assert exit_code == 0, ( + f'b2 --help failed with exit code {exit_code}.\n' + f'This may indicate the buffer overflow bug is not properly handled.\n' + f'Output: {output}\n' + f'See: https://github.com/Backblaze/B2_Command_Line_Tool/issues/1119' + ) + + # Verify help output contains expected content + assert 'b2 ' in output or cli_version in output, ( + f'Help output does not contain expected content.\n' + f'Output: {output}' + )