Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
9 changes: 8 additions & 1 deletion b2/_internal/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,14 @@ def _encode_description(self, value: str):
return textwrap.dedent(value)
else:
encoding = self._get_encoding()
return rst2ansi(value.encode(encoding), output_encoding=encoding)
try:
return rst2ansi(value.encode(encoding), output_encoding=encoding)
except SystemError:
# FALLBACK(PARSER): rst2ansi can raise SystemError on Python 3.14+ due to
# buffer overflow bug in get_terminal_size ioctl call.
# See: https://github.com/Backblaze/B2_Command_Line_Tool/issues/1119
# TODO-REMOVE-BY: When rst2ansi is updated or replaced
return textwrap.dedent(value)

def _make_short_description(self, usage: str, raw_description: str) -> str:
if usage:
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
57 changes: 57 additions & 0 deletions test/integration/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
# License https://www.backblaze.com/using_b2_code.html
#
######################################################################
import os
import platform
import re
import subprocess

import pytest


def test_help(cli_version):
p = subprocess.run(
Expand All @@ -26,3 +29,57 @@ 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


@pytest.mark.skipif(
platform.system() == 'Windows',
reason='PTY tests require Unix-like system',
)
def test_help_with_tty(cli_version):
"""
Test that B2 CLI --help works correctly with a real PTY.

Verifies fix for rst2ansi buffer overflow on Python 3.14+.
See: https://github.com/Backblaze/B2_Command_Line_Tool/issues/1119

NOTE: Works in CI with pytest-xdist, but may not trigger the bug locally.
"""
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(
Comment thread
przadka marked this conversation as resolved.
Outdated
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