From c6cbc7dfa51b6109c8a54c0bf3d6a509f89d356c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Thu, 5 Aug 2021 20:58:43 +0200 Subject: [PATCH] Uniformize UTF-8 naming (#1115) * Uniformize UTF-8 naming Replace `utf8` -> `utf-8` everywhere. It should have no impact, `utf8` is an alias of `utf-8` [1]. [1] https://github.com/python/cpython/blob/ee03bad25e83b00ba5fc2a0265b48c6286e6b3f7/Lib/encodings/aliases.py#L534 * Always specify the encoding Let's be explicit over implicit. And prevent future warnings from PEP-597 [1]. [1] https://www.python.org/dev/peps/pep-0597/#using-the-default-encoding-is-a-common-mistake * Update `UTF8` constant (`utf-8` -> `utf_8`) * Remove default argument from `str.encode()` and `bytes.decode()` * Clean-up --- httpie/cli/argparser.py | 2 +- httpie/cli/requestitems.py | 2 +- httpie/client.py | 4 ++-- httpie/config.py | 5 +++-- httpie/constants.py | 2 ++ httpie/context.py | 7 ++++--- httpie/models.py | 11 ++++++----- httpie/output/streams.py | 7 ++++--- httpie/plugins/builtin.py | 2 +- httpie/sessions.py | 2 +- tests/fixtures/__init__.py | 6 ++++-- tests/test_cli.py | 2 +- tests/test_config.py | 3 ++- tests/test_docs.py | 2 +- tests/test_downloads.py | 2 +- tests/test_httpie.py | 3 ++- tests/test_output.py | 5 +++-- tests/test_sessions.py | 13 +++++++------ tests/test_unicode.py | 8 ++++---- tests/test_uploads.py | 4 ++-- tests/utils/__init__.py | 4 ++-- 21 files changed, 54 insertions(+), 42 deletions(-) create mode 100644 httpie/constants.py diff --git a/httpie/cli/argparser.py b/httpie/cli/argparser.py index 73e3d27b..d3b2fe7e 100644 --- a/httpie/cli/argparser.py +++ b/httpie/cli/argparser.py @@ -301,7 +301,7 @@ class HTTPieArgumentParser(argparse.ArgumentParser): """ self._ensure_one_data_source(self.has_stdin_data, self.args.data, self.args.files) - self.args.data = data.encode('utf-8') + self.args.data = data.encode() def _ensure_one_data_source(self, *other_sources): """There can only be one source of input request data. diff --git a/httpie/cli/requestitems.py b/httpie/cli/requestitems.py index 9aa00f99..30b49562 100644 --- a/httpie/cli/requestitems.py +++ b/httpie/cli/requestitems.py @@ -144,7 +144,7 @@ def load_text_file(item: KeyValueArg) -> str: except UnicodeDecodeError: raise ParseError( f'{item.orig!r}: cannot embed the content of {item.value!r},' - ' not a UTF8 or ASCII-encoded text file' + ' not a UTF-8 or ASCII-encoded text file' ) diff --git a/httpie/client.py b/httpie/client.py index af486722..d9a46d6f 100644 --- a/httpie/client.py +++ b/httpie/client.py @@ -24,7 +24,7 @@ from .utils import get_expired_cookies, repr_dict urllib3.disable_warnings() -FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8' +FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=UTF-8' JSON_CONTENT_TYPE = 'application/json' JSON_ACCEPT = f'{JSON_CONTENT_TYPE}, */*;q=0.5' DEFAULT_UA = f'HTTPie/{__version__}' @@ -188,7 +188,7 @@ def finalize_headers(headers: RequestHeadersDict) -> RequestHeadersDict: value = value.strip() if isinstance(value, str): # See - value = value.encode('utf8') + value = value.encode() final_headers[name] = value return final_headers diff --git a/httpie/config.py b/httpie/config.py index ec4ad301..d8c664a8 100644 --- a/httpie/config.py +++ b/httpie/config.py @@ -5,6 +5,7 @@ from typing import Union from . import __version__ from .compat import is_windows +from .constants import UTF8 ENV_XDG_CONFIG_HOME = 'XDG_CONFIG_HOME' @@ -79,7 +80,7 @@ class BaseConfigDict(dict): def load(self): config_type = type(self).__name__.lower() try: - with self.path.open() as f: + with self.path.open(encoding=UTF8) as f: try: data = json.load(f) except ValueError as e: @@ -111,7 +112,7 @@ class BaseConfigDict(dict): ensure_ascii=True, ) try: - self.path.write_text(json_string + '\n') + self.path.write_text(json_string + '\n', encoding=UTF8) except OSError: if not fail_silently: raise diff --git a/httpie/constants.py b/httpie/constants.py new file mode 100644 index 00000000..8b13f5dc --- /dev/null +++ b/httpie/constants.py @@ -0,0 +1,2 @@ +# UTF-8 encoding name +UTF8 = 'utf-8' diff --git a/httpie/context.py b/httpie/context.py index b0f32c26..a0b87b8e 100644 --- a/httpie/context.py +++ b/httpie/context.py @@ -11,6 +11,7 @@ except ImportError: from .compat import is_windows from .config import DEFAULT_CONFIG_DIR, Config, ConfigFileError +from .constants import UTF8 from .utils import repr_dict @@ -70,10 +71,10 @@ class Environment: self._orig_stderr = self.stderr self._devnull = devnull - # Keyword arguments > stream.encoding > default utf8 + # Keyword arguments > stream.encoding > default UTF-8 if self.stdin and self.stdin_encoding is None: self.stdin_encoding = getattr( - self.stdin, 'encoding', None) or 'utf8' + self.stdin, 'encoding', None) or UTF8 if self.stdout_encoding is None: actual_stdout = self.stdout if is_windows: @@ -83,7 +84,7 @@ class Environment: # noinspection PyUnresolvedReferences actual_stdout = self.stdout.wrapped self.stdout_encoding = getattr( - actual_stdout, 'encoding', None) or 'utf8' + actual_stdout, 'encoding', None) or UTF8 def __str__(self): defaults = dict(type(self).__dict__) diff --git a/httpie/models.py b/httpie/models.py index b56670f7..f4ddb03b 100644 --- a/httpie/models.py +++ b/httpie/models.py @@ -2,6 +2,7 @@ from abc import ABCMeta, abstractmethod from typing import Iterable, Optional from urllib.parse import urlsplit +from .constants import UTF8 from .utils import split_cookies @@ -39,7 +40,7 @@ class HTTPMessage(metaclass=ABCMeta): """Return the message content type.""" ct = self._orig.headers.get('Content-Type', '') if not isinstance(ct, str): - ct = ct.decode('utf8') + ct = ct.decode() return ct @@ -83,7 +84,7 @@ class HTTPResponse(HTTPMessage): @property def encoding(self): - return self._orig.encoding or 'utf8' + return self._orig.encoding or UTF8 @property def body(self): @@ -116,7 +117,7 @@ class HTTPRequest(HTTPMessage): headers['Host'] = url.netloc.split('@')[-1] headers = [ - f'{name}: {value if isinstance(value, str) else value.decode("utf-8")}' + f'{name}: {value if isinstance(value, str) else value.decode()}' for name, value in headers.items() ] @@ -126,12 +127,12 @@ class HTTPRequest(HTTPMessage): @property def encoding(self): - return 'utf8' + return UTF8 @property def body(self): body = self._orig.body if isinstance(body, str): # Happens with JSON/form request data parsed from the command line. - body = body.encode('utf8') + body = body.encode() return body or b'' diff --git a/httpie/output/streams.py b/httpie/output/streams.py index 4fe94acd..44269e3b 100644 --- a/httpie/output/streams.py +++ b/httpie/output/streams.py @@ -3,6 +3,7 @@ from itertools import chain from typing import Callable, Iterable, Union from ..context import Environment +from ..constants import UTF8 from ..models import HTTPMessage from .processing import Conversion, Formatting @@ -49,7 +50,7 @@ class BaseStream(metaclass=ABCMeta): def get_headers(self) -> bytes: """Return the headers' bytes.""" - return self.msg.headers.encode('utf8') + return self.msg.headers.encode() @abstractmethod def iter_body(self) -> Iterable[bytes]: @@ -105,8 +106,8 @@ class EncodedStream(BaseStream): else: # Preserve the message encoding. output_encoding = self.msg.encoding - # Default to utf8 when unsure. - self.output_encoding = output_encoding or 'utf8' + # Default to UTF-8 when unsure. + self.output_encoding = output_encoding or UTF8 def iter_body(self) -> Iterable[bytes]: for line, lf in self.msg.iter_lines(self.CHUNK_SIZE): diff --git a/httpie/plugins/builtin.py b/httpie/plugins/builtin.py index 085d4d29..e9363822 100644 --- a/httpie/plugins/builtin.py +++ b/httpie/plugins/builtin.py @@ -30,7 +30,7 @@ class HTTPBasicAuth(requests.auth.HTTPBasicAuth): @staticmethod def make_header(username: str, password: str) -> str: credentials = f'{username}:{password}' - token = b64encode(credentials.encode('utf-8')).strip().decode('latin1') + token = b64encode(credentials.encode()).strip().decode('latin1') return f'Basic {token}' diff --git a/httpie/sessions.py b/httpie/sessions.py index e9691307..0d800c0f 100644 --- a/httpie/sessions.py +++ b/httpie/sessions.py @@ -78,7 +78,7 @@ class Session(BaseConfigDict): continue # Ignore explicitly unset headers if type(value) is not str: - value = value.decode('utf8') + value = value.decode() if name.lower() == 'user-agent' and value.startswith('HTTPie/'): continue diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index e14ec209..240326c7 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,6 +1,8 @@ """Test data""" from pathlib import Path +from httpie.constants import UTF8 + def patharg(path): """ @@ -23,9 +25,9 @@ JSON_FILE_PATH_ARG = patharg(JSON_FILE_PATH) # Strip because we don't want new lines in the data so that we can # easily count occurrences also when embedded in JSON (where the new # line would be escaped). -FILE_CONTENT = FILE_PATH.read_text('utf8').strip() +FILE_CONTENT = FILE_PATH.read_text(encoding=UTF8).strip() -JSON_FILE_CONTENT = JSON_FILE_PATH.read_text('utf8') +JSON_FILE_CONTENT = JSON_FILE_PATH.read_text(encoding=UTF8) BIN_FILE_CONTENT = BIN_FILE_PATH.read_bytes() UNICODE = FILE_CONTENT diff --git a/tests/test_cli.py b/tests/test_cli.py index be21b61e..6d4998f9 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -118,7 +118,7 @@ class TestItemParsing: # Parsed file fields assert 'file' in items.files assert (items.files['file'][1].read().strip(). - decode('utf8') == FILE_CONTENT) + decode() == FILE_CONTENT) def test_multiple_file_fields_with_same_field_name(self): items = RequestItems.from_args([ diff --git a/tests/test_config.py b/tests/test_config.py index 7b990edb..680b16b9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,6 +4,7 @@ import pytest from _pytest.monkeypatch import MonkeyPatch from httpie.compat import is_windows +from httpie.constants import UTF8 from httpie.config import ( Config, DEFAULT_CONFIG_DIRNAME, DEFAULT_RELATIVE_LEGACY_CONFIG_DIR, DEFAULT_RELATIVE_XDG_CONFIG_HOME, DEFAULT_WINDOWS_CONFIG_DIR, @@ -25,7 +26,7 @@ def test_default_options(httpbin): def test_config_file_not_valid(httpbin): env = MockEnvironment() env.create_temp_config_dir() - (env.config_dir / Config.FILENAME).write_text('{invalid json}') + (env.config_dir / Config.FILENAME).write_text('{invalid json}', encoding=UTF8) r = http(httpbin + '/get', env=env) assert HTTP_OK in r assert 'http: warning' in r.stderr diff --git a/tests/test_docs.py b/tests/test_docs.py index 480fcdef..ef45d795 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -66,4 +66,4 @@ def test_rst_file_syntax(filename): shell=True, ) err = p.communicate()[1] - assert p.returncode == 0, err.decode('utf8') + assert p.returncode == 0, err.decode() diff --git a/tests/test_downloads.py b/tests/test_downloads.py index 48b38940..9b6d38f9 100644 --- a/tests/test_downloads.py +++ b/tests/test_downloads.py @@ -66,7 +66,7 @@ class TestDownloadUtils: ) assert 'foo.html' == filename_from_url( url='http://example.org/foo', - content_type='text/html; charset=utf8' + content_type='text/html; charset=UTF-8' ) assert 'foo' == filename_from_url( url='http://example.org/foo', diff --git a/tests/test_httpie.py b/tests/test_httpie.py index 7df4d478..e45e0d29 100644 --- a/tests/test_httpie.py +++ b/tests/test_httpie.py @@ -9,6 +9,7 @@ import httpie.__main__ from .fixtures import FILE_CONTENT, FILE_PATH from httpie.cli.exceptions import ParseError from httpie.context import Environment +from httpie.constants import UTF8 from httpie.status import ExitStatus from .utils import HTTP_OK, MockEnvironment, StdinBytesIO, http @@ -130,7 +131,7 @@ def test_form_POST_file_redirected_stdin(httpbin): """ - with open(FILE_PATH): + with open(FILE_PATH, encoding=UTF8): r = http( '--form', 'POST', diff --git a/tests/test_output.py b/tests/test_output.py index 9cf667b0..ee3a85c4 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -15,6 +15,7 @@ from httpie.cli.argtypes import ( parse_format_options, ) from httpie.cli.definition import parser +from httpie.constants import UTF8 from httpie.output.formatters.colors import get_lexer from httpie.status import ExitStatus from .utils import COLOR, CRLF, HTTP_OK, MockEnvironment, http @@ -30,7 +31,7 @@ def test_output_option(tmp_path, httpbin, stdout_isatty): assert r == '' expected_body = urlopen(url).read().decode() - actual_body = output_filename.read_text() + actual_body = output_filename.read_text(encoding=UTF8) assert actual_body == expected_body @@ -125,7 +126,7 @@ class TestQuietFlag: assert env.stdout is env.devnull else: assert env.stdout is not env.devnull # --output swaps stdout. - assert output_path.read_text() == output + assert output_path.read_text(encoding=UTF8) == output finally: os.chdir(orig_cwd) diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 9e4fec75..23e1dd9c 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -7,6 +7,7 @@ from unittest import mock import pytest from .fixtures import FILE_PATH_ARG, UNICODE +from httpie.constants import UTF8 from httpie.plugins import AuthPlugin from httpie.plugins.builtin import HTTPBasicAuth from httpie.plugins.registry import plugin_manager @@ -52,7 +53,7 @@ class CookieTestBase: } } self.session_path = self.config_dir / 'test-session.json' - self.session_path.write_text(json.dumps(orig_session)) + self.session_path.write_text(json.dumps(orig_session), encoding=UTF8) def teardown_method(self, method): shutil.rmtree(self.config_dir) @@ -192,7 +193,7 @@ class TestSession(SessionTestBase): # FIXME: Authorization *sometimes* is not present assert (r2.json['headers']['Authorization'] == HTTPBasicAuth.make_header('test', UNICODE)) - # httpbin doesn't interpret utf8 headers + # httpbin doesn't interpret UTF-8 headers assert UNICODE in r2 def test_session_default_header_value_overwritten(self, httpbin): @@ -309,7 +310,7 @@ class TestSession(SessionTestBase): Plugin.auth_type, '--auth', 'user:password', ) - updated_session = json.loads(self.session_path.read_text()) + updated_session = json.loads(self.session_path.read_text(encoding=UTF8)) assert updated_session['auth']['type'] == 'test-saved' assert updated_session['auth']['raw_auth'] == "user:password" plugin_manager.unregister(Plugin) @@ -338,7 +339,7 @@ class TestExpiredCookies(CookieTestBase): ) assert 'Cookie: cookie1=foo; cookie2=foo' in r - updated_session = json.loads(self.session_path.read_text()) + updated_session = json.loads(self.session_path.read_text(encoding=UTF8)) assert 'cookie1' in updated_session['cookies'] assert 'cookie2' not in updated_session['cookies'] @@ -432,7 +433,7 @@ class TestCookieStorage(CookieTestBase): # Note: cookies in response are in alphabetical order assert f'Cookie: {expected}' in r - updated_session = json.loads(self.session_path.read_text()) + updated_session = json.loads(self.session_path.read_text(encoding=UTF8)) for name, value in new_cookies_dict.items(): assert name, value in updated_session['cookies'] assert 'Cookie' not in updated_session['headers'] @@ -473,6 +474,6 @@ class TestCookieStorage(CookieTestBase): httpbin.url + set_cookie, 'Cookie:' + cli_cookie, ) - updated_session = json.loads(self.session_path.read_text()) + updated_session = json.loads(self.session_path.read_text(encoding=UTF8)) assert updated_session['cookies']['cookie1']['value'] == expected diff --git a/tests/test_unicode.py b/tests/test_unicode.py index 22506559..d1ea8172 100644 --- a/tests/test_unicode.py +++ b/tests/test_unicode.py @@ -7,13 +7,13 @@ from .fixtures import UNICODE def test_unicode_headers(httpbin): - # httpbin doesn't interpret utf8 headers + # httpbin doesn't interpret UFT-8 headers r = http(httpbin.url + '/headers', f'Test:{UNICODE}') assert HTTP_OK in r def test_unicode_headers_verbose(httpbin): - # httpbin doesn't interpret utf8 headers + # httpbin doesn't interpret UTF-8 headers r = http('--verbose', httpbin.url + '/headers', f'Test:{UNICODE}') assert HTTP_OK in r assert UNICODE in r @@ -98,14 +98,14 @@ def test_unicode_url_verbose(httpbin): def test_unicode_basic_auth(httpbin): # it doesn't really authenticate us because httpbin - # doesn't interpret the utf8-encoded auth + # doesn't interpret the UTF-8-encoded auth http('--verbose', '--auth', f'test:{UNICODE}', f'{httpbin.url}/basic-auth/test/{UNICODE}') def test_unicode_digest_auth(httpbin): # it doesn't really authenticate us because httpbin - # doesn't interpret the utf8-encoded auth + # doesn't interpret the UTF-8-encoded auth http('--auth-type=digest', '--auth', f'test:{UNICODE}', f'{httpbin.url}/digest-auth/auth/test/{UNICODE}') diff --git a/tests/test_uploads.py b/tests/test_uploads.py index ca424020..2c701477 100644 --- a/tests/test_uploads.py +++ b/tests/test_uploads.py @@ -247,10 +247,10 @@ class TestRequestBodyFromFilePath: self, httpbin): r = http('--verbose', 'POST', httpbin.url + '/post', '@' + FILE_PATH_ARG, - 'Content-Type:text/plain; charset=utf8') + 'Content-Type:text/plain; charset=UTF-8') assert HTTP_OK in r assert FILE_CONTENT in r - assert 'Content-Type: text/plain; charset=utf8' in r + assert 'Content-Type: text/plain; charset=UTF-8' in r def test_request_body_from_file_by_path_no_field_name_allowed( self, httpbin): diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 3713067c..bf9c8304 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -281,14 +281,14 @@ def http( output = stdout.read() devnull_output = devnull.read() try: - output = output.decode('utf8') + output = output.decode() except UnicodeDecodeError: r = BytesCLIResponse(output) else: r = StrCLIResponse(output) try: - devnull_output = devnull_output.decode('utf8') + devnull_output = devnull_output.decode() except Exception: pass