diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d98b63ae69..9574e16adf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,6 +33,16 @@ jobs: - '3.8' - '3.7' pyopenssl: [0, 1] + exclude: + - os: ubuntu-latest + python-version: '3.7' + include: + - os: ubuntu-22.04 + python-version: '3.7' + pyopenssl: 0 + - os: ubuntu-22.04 + python-version: '3.7' + pyopenssl: 1 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -42,7 +52,7 @@ jobs: - name: Windows setup if: matrix.os == 'windows-latest' run: | - python -m pip install --upgrade pip wheel + python -m pip install --upgrade "pip<25.3; python_version<'3.10'" "pip; python_version>='3.10'" wheel python -m pip install --upgrade '.[dev]' python -m pytest --verbose ./httpie ./tests env: diff --git a/Makefile b/Makefile index a2a80a1778..eb4e1e8158 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ install: venv install-reqs install-reqs: @echo $(H1)Updating package tools$(H1END) - $(VENV_PIP) install --upgrade pip wheel build + $(VENV_PIP) install --upgrade "pip<25.3; python_version<'3.10'" "pip; python_version>='3.10'" wheel build @echo $(H1)Installing dev requirements$(H1END) $(VENV_PIP) install --upgrade '.[dev]' '.[test]' diff --git a/httpie/cli/argparser.py b/httpie/cli/argparser.py index 9bf09b3b73..bc8515ce0b 100644 --- a/httpie/cli/argparser.py +++ b/httpie/cli/argparser.py @@ -121,8 +121,22 @@ def _print_message(self, message, file=None): }.get(file, file) if not hasattr(file, 'buffer') and isinstance(message, str): - message = message.encode(env.stdout_encoding) - super()._print_message(message, file) + encoding = getattr(env, 'stdout_encoding', None) or sys.getdefaultencoding() + message = message.encode(encoding, errors='backslashreplace') + try: + super()._print_message(message, file) + except UnicodeEncodeError: + if not isinstance(message, str): + raise + encoding = ( + getattr(file, 'encoding', None) + or getattr(env, 'stdout_encoding', None) + or sys.getdefaultencoding() + ) + message = message.encode( + encoding, errors='backslashreplace' + ).decode(encoding) + super()._print_message(message, file) class HTTPieManagerArgumentParser(BaseHTTPieArgumentParser): diff --git a/setup.cfg b/setup.cfg index 85490981aa..968755d6ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,7 +90,7 @@ dev = flake8-deprecated flake8-mutable flake8-tuple - pyopenssl + pyopenssl<26 pytest-cov pyyaml twine diff --git a/tests/test_cli_ui.py b/tests/test_cli_ui.py index bb744cdc4e..8edbd522f7 100644 --- a/tests/test_cli_ui.py +++ b/tests/test_cli_ui.py @@ -30,6 +30,11 @@ error_msg="argument --pretty: invalid choice: '$invalid' (choose from 'all', 'colors', 'format', 'none')" ) +NAKED_HELP_MESSAGE_PRETTY_WITH_INVALID_ARG_UNQUOTED = NAKED_BASE_TEMPLATE.format( + extra_args="--pretty {all, colors, format, none} ", + error_msg="argument --pretty: invalid choice: '$invalid' (choose from all, colors, format, none)" +) + PREDEFINED_TERMINAL_SIZE = (200, 100) @@ -57,9 +62,18 @@ def fake_terminal_size(*args, **kwargs): ([], NAKED_HELP_MESSAGE), (['--pretty'], NAKED_HELP_MESSAGE_PRETTY_WITH_NO_ARG), (['pie.dev', '--pretty'], NAKED_HELP_MESSAGE_PRETTY_WITH_NO_ARG), - (['--pretty', '$invalid'], NAKED_HELP_MESSAGE_PRETTY_WITH_INVALID_ARG), + ( + ['--pretty', '$invalid'], + ( + NAKED_HELP_MESSAGE_PRETTY_WITH_INVALID_ARG, + NAKED_HELP_MESSAGE_PRETTY_WITH_INVALID_ARG_UNQUOTED, + ) + ), ] ) def test_naked_invocation(ignore_terminal_size, args, expected_msg): result = http(*args, tolerate_error_exit_status=True) - assert result.stderr == expected_msg + if isinstance(expected_msg, tuple): + assert result.stderr in expected_msg + else: + assert result.stderr == expected_msg diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 62814161ed..0e4938fc8c 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -14,7 +14,7 @@ CHARSET_TEXT_PAIRS = [ - ('big5', '卷首卷首卷首卷首卷卷首卷首卷首卷首卷首卷首卷首卷首卷首卷首卷首卷首卷首'), + ('big5', '中文測試內容,這是一段使用繁體中文撰寫的文字,用來確認 Big5 編碼可以被正確偵測。'), ('windows-1250', 'Všichni lidé jsou si rovni. Všichni lidé jsou si rovni.'), (UTF8, 'Všichni lidé jsou si rovni. Všichni lidé jsou si rovni.'), ] diff --git a/tests/test_httpie.py b/tests/test_httpie.py index 5824340cda..efddd8055a 100644 --- a/tests/test_httpie.py +++ b/tests/test_httpie.py @@ -39,6 +39,17 @@ def test_help(): assert 'https://github.com/httpie/cli/issues' in r +def test_help_with_ascii_stdout(): + stdout = io.TextIOWrapper(io.BytesIO(), encoding='ascii') + env = MockEnvironment(stdout=stdout, stdout_isatty=True) + + r = http('--help', env=env, tolerate_error_exit_status=True) + + assert r.exit_status == ExitStatus.SUCCESS + r.encode('ascii') + assert 'https://github.com/httpie/cli/issues' in r + + def test_version(): r = http('--version', tolerate_error_exit_status=True) assert r.exit_status == ExitStatus.SUCCESS