Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions changelog.d/+python-3.14-support.infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added Python 3.14 support to CI/CD pipeline and test matrix.
1 change: 1 addition & 0 deletions changelog.d/1119.fixed.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
'3.11',
'3.12',
'3.13',
'3.14',
]
if NOX_PYTHONS is None
else NOX_PYTHONS.split(',')
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
67 changes: 67 additions & 0 deletions test/integration/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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} <command> --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 <command>' in output or cli_version in output, (
f'Help output does not contain expected content.\n'
f'Output: {output}'
)
Loading