Introduce a mode to suppress all warnings (#1283)

This commit is contained in:
Batuhan Taskaya 2022-03-07 15:40:35 +03:00 committed by GitHub
parent c901e70463
commit 55087a901e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 6 deletions

View File

@ -8,7 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/httpie/issues/1285)) - Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/httpie/issues/1285))
- Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/httpie/issues/1300)) - Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/httpie/issues/1300))
- Improved regulation of top-level arrays. ([#1292](https://github.com/httpie/httpie/commit/225dccb2186f14f871695b6c4e0bfbcdb2e3aa28)) - Improved regulation of top-level arrays. ([#1292](https://github.com/httpie/httpie/commit/225dccb2186f14f871695b6c4e0bfbcdb2e3aa28))
- Double `--quiet` flags will now suppress all python level warnings. ([#1271](https://github.com/httpie/httpie/issues/1271))
## [3.0.2](https://github.com/httpie/httpie/compare/3.0.1...3.0.2) (2022-01-24) ## [3.0.2](https://github.com/httpie/httpie/compare/3.0.1...3.0.2) (2022-01-24)

View File

@ -230,9 +230,11 @@ class HTTPieArgumentParser(BaseHTTPieArgumentParser):
self.env.stdout_isatty = False self.env.stdout_isatty = False
if self.args.quiet: if self.args.quiet:
self.env.quiet = self.args.quiet
self.env.stderr = self.env.devnull self.env.stderr = self.env.devnull
if not (self.args.output_file_specified and not self.args.download): if not (self.args.output_file_specified and not self.args.download):
self.env.stdout = self.env.devnull self.env.stdout = self.env.devnull
self.env.apply_warnings_filter()
def _process_auth(self): def _process_auth(self):
# TODO: refactor & simplify this method. # TODO: refactor & simplify this method.

View File

@ -1,8 +1,10 @@
import sys import sys
import os import os
import warnings
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import Iterator, IO, Optional from typing import Iterator, IO, Optional
from enum import Enum
try: try:
@ -17,6 +19,17 @@ from .encoding import UTF8
from .utils import repr_dict from .utils import repr_dict
class Levels(str, Enum):
WARNING = 'warning'
ERROR = 'error'
DISPLAY_THRESHOLDS = {
Levels.WARNING: 2,
Levels.ERROR: float('inf'), # Never hide errors.
}
class Environment: class Environment:
""" """
Information about the execution context Information about the execution context
@ -87,6 +100,8 @@ class Environment:
self.stdout_encoding = getattr( self.stdout_encoding = getattr(
actual_stdout, 'encoding', None) or UTF8 actual_stdout, 'encoding', None) or UTF8
self.quiet = kwargs.pop('quiet', 0)
def __str__(self): def __str__(self):
defaults = dict(type(self).__dict__) defaults = dict(type(self).__dict__)
actual = dict(defaults) actual = dict(defaults)
@ -134,6 +149,14 @@ class Environment:
self.stdout = original_stdout self.stdout = original_stdout
self.stderr = original_stderr self.stderr = original_stderr
def log_error(self, msg, level='error'): def log_error(self, msg: str, level: Levels = Levels.ERROR) -> None:
assert level in ['error', 'warning'] if self.stdout_isatty and self.quiet >= DISPLAY_THRESHOLDS[level]:
self._orig_stderr.write(f'\n{self.program_name}: {level}: {msg}\n\n') stderr = self.stderr # Not directly /dev/null, since stderr might be mocked
else:
stderr = self._orig_stderr
stderr.write(f'\n{self.program_name}: {level}: {msg}\n\n')
def apply_warnings_filter(self) -> None:
if self.quiet >= DISPLAY_THRESHOLDS[Levels.WARNING]:
warnings.simplefilter("ignore")

View File

@ -13,7 +13,7 @@ from . import __version__ as httpie_version
from .cli.constants import OUT_REQ_BODY from .cli.constants import OUT_REQ_BODY
from .cli.nested_json import HTTPieSyntaxError from .cli.nested_json import HTTPieSyntaxError
from .client import collect_messages from .client import collect_messages
from .context import Environment from .context import Environment, Levels
from .downloads import Downloader from .downloads import Downloader
from .models import ( from .models import (
RequestsMessageKind, RequestsMessageKind,
@ -221,7 +221,7 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
if args.check_status or downloader: if args.check_status or downloader:
exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow) exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow)
if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet == 1): if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet == 1):
env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level='warning') env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level=Levels.WARNING)
write_message(requests_message=message, env=env, args=args, output_options=output_options._replace( write_message(requests_message=message, env=env, args=args, output_options=output_options._replace(
body=do_write_body body=do_write_body
)) ))

View File

@ -5,6 +5,7 @@ from unittest import mock
import json import json
import os import os
import io import io
import warnings
from urllib.request import urlopen from urllib.request import urlopen
import pytest import pytest
@ -90,6 +91,31 @@ class TestQuietFlag:
) )
assert 'http: warning: HTTP 500' in r.stderr assert 'http: warning: HTTP 500' in r.stderr
@mock.patch('httpie.core.program')
@pytest.mark.parametrize('flags, expected_warnings', [
([], 1),
(['-q'], 1),
(['-qq'], 0),
])
def test_quiet_on_python_warnings(self, test_patch, httpbin, flags, expected_warnings):
def warn_and_run(*args, **kwargs):
warnings.warn('warning!!')
return ExitStatus.SUCCESS
test_patch.side_effect = warn_and_run
with pytest.warns(None) as record:
http(*flags, httpbin + '/get')
assert len(record) == expected_warnings
def test_double_quiet_on_error(self, httpbin):
r = http(
'-qq', '--check-status', '$$$this.does.not.exist$$$',
tolerate_error_exit_status=True,
)
assert not r
assert 'Couldnt resolve the given hostname' in r.stderr
@pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS) @pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS)
@mock.patch('httpie.cli.argtypes.AuthCredentials._getpass', @mock.patch('httpie.cli.argtypes.AuthCredentials._getpass',
new=lambda self, prompt: 'password') new=lambda self, prompt: 'password')

View File

@ -5,6 +5,7 @@ import sys
import time import time
import json import json
import tempfile import tempfile
import warnings
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import Any, Optional, Union, List, Iterable from typing import Any, Optional, Union, List, Iterable
@ -96,6 +97,7 @@ class MockEnvironment(Environment):
def cleanup(self): def cleanup(self):
self.stdout.close() self.stdout.close()
self.stderr.close() self.stderr.close()
warnings.resetwarnings()
if self._delete_config_dir: if self._delete_config_dir:
assert self._temp_dir in self.config_dir.parents assert self._temp_dir in self.config_dir.parents
from shutil import rmtree from shutil import rmtree