From cb8197ad6d9c6bf21e2c01ba3d373445f3b77eab Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sun, 13 Jul 2025 07:55:11 +0100 Subject: [PATCH 1/6] Raise valueerrors --- Lib/encodings/base64_codec.py | 6 ++++-- Lib/encodings/bz2_codec.py | 14 ++++++++++---- Lib/encodings/hex_codec.py | 6 ++++-- Lib/encodings/quopri_codec.py | 6 ++++-- Lib/encodings/uu_codec.py | 6 ++++-- Lib/encodings/zlib_codec.py | 14 ++++++++++---- Lib/test/test_codecs.py | 12 ++++++++++++ .../2025-07-13-07-54-49.gh-issue-62040.jryEkb.rst | 4 ++++ 8 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-13-07-54-49.gh-issue-62040.jryEkb.rst diff --git a/Lib/encodings/base64_codec.py b/Lib/encodings/base64_codec.py index 8e7703b3b6072d..e740d2bd1c8a1c 100644 --- a/Lib/encodings/base64_codec.py +++ b/Lib/encodings/base64_codec.py @@ -11,11 +11,13 @@ ### Codec APIs def base64_encode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') return (base64.encodebytes(input), len(input)) def base64_decode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') return (base64.decodebytes(input), len(input)) class Codec(codecs.Codec): diff --git a/Lib/encodings/bz2_codec.py b/Lib/encodings/bz2_codec.py index fd9495e341baee..2f732d9344ac75 100644 --- a/Lib/encodings/bz2_codec.py +++ b/Lib/encodings/bz2_codec.py @@ -10,14 +10,20 @@ import codecs import bz2 # this codec needs the optional bz2 module ! +### Codec Helpers + +def _assert_strict(errors): + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + ### Codec APIs def bz2_encode(input, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) return (bz2.compress(input), len(input)) def bz2_decode(input, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) return (bz2.decompress(input), len(input)) class Codec(codecs.Codec): @@ -28,7 +34,7 @@ def decode(self, input, errors='strict'): class IncrementalEncoder(codecs.IncrementalEncoder): def __init__(self, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) self.errors = errors self.compressobj = bz2.BZ2Compressor() @@ -44,7 +50,7 @@ def reset(self): class IncrementalDecoder(codecs.IncrementalDecoder): def __init__(self, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) self.errors = errors self.decompressobj = bz2.BZ2Decompressor() diff --git a/Lib/encodings/hex_codec.py b/Lib/encodings/hex_codec.py index 9fb1072804456a..ae227833a80083 100644 --- a/Lib/encodings/hex_codec.py +++ b/Lib/encodings/hex_codec.py @@ -11,11 +11,13 @@ ### Codec APIs def hex_encode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') return (binascii.b2a_hex(input), len(input)) def hex_decode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') return (binascii.a2b_hex(input), len(input)) class Codec(codecs.Codec): diff --git a/Lib/encodings/quopri_codec.py b/Lib/encodings/quopri_codec.py index 496cb7655d032d..9af22662881f83 100644 --- a/Lib/encodings/quopri_codec.py +++ b/Lib/encodings/quopri_codec.py @@ -8,14 +8,16 @@ from io import BytesIO def quopri_encode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') f = BytesIO(input) g = BytesIO() quopri.encode(f, g, quotetabs=True) return (g.getvalue(), len(input)) def quopri_decode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') f = BytesIO(input) g = BytesIO() quopri.decode(f, g) diff --git a/Lib/encodings/uu_codec.py b/Lib/encodings/uu_codec.py index 4e58c62fe9ef0f..4e9b57c8de9c84 100644 --- a/Lib/encodings/uu_codec.py +++ b/Lib/encodings/uu_codec.py @@ -14,7 +14,8 @@ ### Codec APIs def uu_encode(input, errors='strict', filename='', mode=0o666): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') infile = BytesIO(input) outfile = BytesIO() read = infile.read @@ -35,7 +36,8 @@ def uu_encode(input, errors='strict', filename='', mode=0o666): return (outfile.getvalue(), len(input)) def uu_decode(input, errors='strict'): - assert errors == 'strict' + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') infile = BytesIO(input) outfile = BytesIO() readline = infile.readline diff --git a/Lib/encodings/zlib_codec.py b/Lib/encodings/zlib_codec.py index 95908a4b4a13a1..807d8b41ce40b2 100644 --- a/Lib/encodings/zlib_codec.py +++ b/Lib/encodings/zlib_codec.py @@ -8,14 +8,20 @@ import codecs import zlib # this codec needs the optional zlib module ! +### Codec Helpers + +def _assert_strict(errors): + if errors != 'strict': + raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + ### Codec APIs def zlib_encode(input, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) return (zlib.compress(input), len(input)) def zlib_decode(input, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) return (zlib.decompress(input), len(input)) class Codec(codecs.Codec): @@ -26,7 +32,7 @@ def decode(self, input, errors='strict'): class IncrementalEncoder(codecs.IncrementalEncoder): def __init__(self, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) self.errors = errors self.compressobj = zlib.compressobj() @@ -42,7 +48,7 @@ def reset(self): class IncrementalDecoder(codecs.IncrementalDecoder): def __init__(self, errors='strict'): - assert errors == 'strict' + _assert_strict(errors) self.errors = errors self.decompressobj = zlib.decompressobj() diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index d8666f7290e72e..9b64888b082df8 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3128,6 +3128,18 @@ def test_uu_invalid(self): # Missing "begin" line self.assertRaises(ValueError, codecs.decode, b"", "uu-codec") + def test_invalid_error_input(self): + # decoders/encoders require errors == 'strict' + + for encoding in bytes_transform_encodings: + with self.subTest(encoding=encoding): + encoder = codecs.getencoder(encoding) + decoder = codecs.getdecoder(encoding) + + self.assertRaises(ValueError, encoder, 'in', errors='notstrict') + self.assertRaises(ValueError, decoder, 'in', errors='notstrict') + + # The codec system tries to add notes to exceptions in order to ensure # the error mentions the operation being performed and the codec involved. diff --git a/Misc/NEWS.d/next/Library/2025-07-13-07-54-49.gh-issue-62040.jryEkb.rst b/Misc/NEWS.d/next/Library/2025-07-13-07-54-49.gh-issue-62040.jryEkb.rst new file mode 100644 index 00000000000000..19fe8636b2d3f0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-13-07-54-49.gh-issue-62040.jryEkb.rst @@ -0,0 +1,4 @@ +The ``base64_codec``, ``uu_codec``, ``quopri_codec``, ``hex_codec``, +``zlib_codec`` and ``bz2_codec`` now raise a :exc:`ValueError` when their +decoder/encoder is provided an *errors* parameter that is not equal to +``'strict'``. From f7d77c48413801499e5b431ee2e3a32de140e226 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 24 Jul 2025 10:22:15 +0200 Subject: [PATCH 2/6] Remaining asserts --- Lib/encodings/base64_codec.py | 16 ++++++++++------ Lib/encodings/hex_codec.py | 16 ++++++++++------ Lib/encodings/quopri_codec.py | 12 +++++++++--- Lib/encodings/uu_codec.py | 12 ++++++++---- Lib/test/test_http_cookies.py | 3 ++- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Lib/encodings/base64_codec.py b/Lib/encodings/base64_codec.py index e740d2bd1c8a1c..176cbed1261bcc 100644 --- a/Lib/encodings/base64_codec.py +++ b/Lib/encodings/base64_codec.py @@ -8,16 +8,20 @@ import codecs import base64 -### Codec APIs +### Codec Helpers -def base64_encode(input, errors='strict'): +def _assert_strict(errors): if errors != 'strict': raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + +### Codec APIs + +def base64_encode(input, errors='strict'): + _assert_strict(errors) return (base64.encodebytes(input), len(input)) def base64_decode(input, errors='strict'): - if errors != 'strict': - raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + _assert_strict(errors) return (base64.decodebytes(input), len(input)) class Codec(codecs.Codec): @@ -28,12 +32,12 @@ def decode(self, input, errors='strict'): class IncrementalEncoder(codecs.IncrementalEncoder): def encode(self, input, final=False): - assert self.errors == 'strict' + _assert_strict(self.errors) return base64.encodebytes(input) class IncrementalDecoder(codecs.IncrementalDecoder): def decode(self, input, final=False): - assert self.errors == 'strict' + _assert_strict(self.errors) return base64.decodebytes(input) class StreamWriter(Codec, codecs.StreamWriter): diff --git a/Lib/encodings/hex_codec.py b/Lib/encodings/hex_codec.py index ae227833a80083..c13ae338e2f895 100644 --- a/Lib/encodings/hex_codec.py +++ b/Lib/encodings/hex_codec.py @@ -8,16 +8,20 @@ import codecs import binascii -### Codec APIs +### Codec Helpers -def hex_encode(input, errors='strict'): +def _assert_strict(errors): if errors != 'strict': raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + +### Codec APIs + +def hex_encode(input, errors='strict'): + _assert_strict(errors) return (binascii.b2a_hex(input), len(input)) def hex_decode(input, errors='strict'): - if errors != 'strict': - raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + _assert_strict(errors) return (binascii.a2b_hex(input), len(input)) class Codec(codecs.Codec): @@ -28,12 +32,12 @@ def decode(self, input, errors='strict'): class IncrementalEncoder(codecs.IncrementalEncoder): def encode(self, input, final=False): - assert self.errors == 'strict' + _assert_strict(self.errors) return binascii.b2a_hex(input) class IncrementalDecoder(codecs.IncrementalDecoder): def decode(self, input, final=False): - assert self.errors == 'strict' + _assert_strict(self.errors) return binascii.a2b_hex(input) class StreamWriter(Codec, codecs.StreamWriter): diff --git a/Lib/encodings/quopri_codec.py b/Lib/encodings/quopri_codec.py index 9af22662881f83..183d3531feada4 100644 --- a/Lib/encodings/quopri_codec.py +++ b/Lib/encodings/quopri_codec.py @@ -7,17 +7,23 @@ import quopri from io import BytesIO -def quopri_encode(input, errors='strict'): +### Codec Helpers + +def _assert_strict(errors): if errors != 'strict': raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + +### Codec APIs + +def quopri_encode(input, errors='strict'): + _assert_strict(errors) f = BytesIO(input) g = BytesIO() quopri.encode(f, g, quotetabs=True) return (g.getvalue(), len(input)) def quopri_decode(input, errors='strict'): - if errors != 'strict': - raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + _assert_strict(errors) f = BytesIO(input) g = BytesIO() quopri.decode(f, g) diff --git a/Lib/encodings/uu_codec.py b/Lib/encodings/uu_codec.py index 4e9b57c8de9c84..740bfeaff25ffb 100644 --- a/Lib/encodings/uu_codec.py +++ b/Lib/encodings/uu_codec.py @@ -11,11 +11,16 @@ import binascii from io import BytesIO -### Codec APIs +### Codec Helpers -def uu_encode(input, errors='strict', filename='', mode=0o666): +def _assert_strict(errors): if errors != 'strict': raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + +### Codec APIs + +def uu_encode(input, errors='strict', filename='', mode=0o666): + _assert_strict(errors) infile = BytesIO(input) outfile = BytesIO() read = infile.read @@ -36,8 +41,7 @@ def uu_encode(input, errors='strict', filename='', mode=0o666): return (outfile.getvalue(), len(input)) def uu_decode(input, errors='strict'): - if errors != 'strict': - raise ValueError(f'Unsupported error handling mode: "{errors}" - must be "strict"') + _assert_strict(errors) infile = BytesIO(input) outfile = BytesIO() readline = infile.readline diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index 2fbc142de2fd34..1fe88dc9505d84 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -214,8 +214,9 @@ def test_set_secure_httponly_partitioned_attrs(self): 'Set-Cookie: Customer="WILE_E_COYOTE"; HttpOnly; Partitioned; Secure') def test_samesite_attrs(self): - samesite_values = ['Strict', 'Lax', 'strict', 'lax'] + samesite_values = ['Strict', 'Lax', 'strict', 'lax', 'None', 'none', 'asdasd'] for val in samesite_values: + print(val) with self.subTest(val=val): C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') C['Customer']['samesite'] = val From 289c59014d63534e6f372d013c29b21b9beb8ebc Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 24 Jul 2025 10:23:34 +0200 Subject: [PATCH 3/6] Clean up --- Lib/test/test_http_cookies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index 1fe88dc9505d84..c7da79f0bde2e6 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -214,7 +214,7 @@ def test_set_secure_httponly_partitioned_attrs(self): 'Set-Cookie: Customer="WILE_E_COYOTE"; HttpOnly; Partitioned; Secure') def test_samesite_attrs(self): - samesite_values = ['Strict', 'Lax', 'strict', 'lax', 'None', 'none', 'asdasd'] + samesite_values = ['Strict', 'Lax', 'strict', 'lax'] for val in samesite_values: print(val) with self.subTest(val=val): From 3b73dd670cd03776fb52bd6b79ebede4ae6631e9 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 24 Jul 2025 10:25:27 +0200 Subject: [PATCH 4/6] !fixup Clean up --- Lib/test/test_http_cookies.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index c7da79f0bde2e6..2fbc142de2fd34 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -216,7 +216,6 @@ def test_set_secure_httponly_partitioned_attrs(self): def test_samesite_attrs(self): samesite_values = ['Strict', 'Lax', 'strict', 'lax'] for val in samesite_values: - print(val) with self.subTest(val=val): C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') C['Customer']['samesite'] = val From 59a62aa7e26fe85ae9e2efdf1fb8cee8b05620bb Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 24 Jul 2025 10:34:26 +0200 Subject: [PATCH 5/6] Increase coverage --- Lib/test/test_codecs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 9b64888b082df8..f4d0f8d9ad38c8 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3130,15 +3130,18 @@ def test_uu_invalid(self): def test_invalid_error_input(self): # decoders/encoders require errors == 'strict' - for encoding in bytes_transform_encodings: - with self.subTest(encoding=encoding): + with (self.subTest(encoding=encoding)): encoder = codecs.getencoder(encoding) decoder = codecs.getdecoder(encoding) self.assertRaises(ValueError, encoder, 'in', errors='notstrict') self.assertRaises(ValueError, decoder, 'in', errors='notstrict') + incdev = codecs.getincrementaldecoder(encoding) + if encoding not in ('base64_codec', 'uu_codec', 'quopri_codec', 'hex_codec'): + self.assertRaises(ValueError, incdev, errors='notstrict') + # The codec system tries to add notes to exceptions in order to ensure From a254fec40ea5f05427296444428926533b210822 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 24 Jul 2025 14:25:06 +0200 Subject: [PATCH 6/6] Refactor test --- Lib/test/test_codecs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index f4d0f8d9ad38c8..9ec61518d9cc09 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3129,9 +3129,8 @@ def test_uu_invalid(self): self.assertRaises(ValueError, codecs.decode, b"", "uu-codec") def test_invalid_error_input(self): - # decoders/encoders require errors == 'strict' for encoding in bytes_transform_encodings: - with (self.subTest(encoding=encoding)): + with self.subTest(encoding=encoding): encoder = codecs.getencoder(encoding) decoder = codecs.getdecoder(encoding)