Skip to content

Commit 78f0162

Browse files
committed
Applied minor fix
1 parent 831ab3c commit 78f0162

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎src/codext/__main__.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
"""
55
import argparse
6+
import sys
67

78
from .__common__ import *
89
from .__info__ import __author__, __copyright__, __email__, __license__, __source__, __version__
@@ -27,6 +28,15 @@ def __format_list(items, include=True):
2728
return d
2829

2930

31+
def __is_binary(data):
32+
""" Bytes are binary data if they are neither valid UTF-8 nor latin-1 text (as returned by text codecs). """
33+
try:
34+
data.decode("utf-8")
35+
return False
36+
except UnicodeDecodeError:
37+
return any(b < 32 and b not in b"\t\n\r" or 127 <= b < 160 for b in data)
38+
39+
3040
def __print_tabular(lst, space=4):
3141
try:
3242
cols, _ = os.get_terminal_size()
@@ -200,6 +210,10 @@ def _format_action_invocation(self, action):
200210
if args.outfile:
201211
with codecs.open(args.outfile, 'wb') as f:
202212
f.write(c)
213+
elif isinstance(c, bytes) and __is_binary(c):
214+
# write binary data as is, as printing it would re-encode as UTF-8 the bytes that are not valid UTF-8
215+
sys.stdout.buffer.write(c)
216+
sys.stdout.flush()
203217
else:
204218
print(ensure_str(c or "Could not %scode :-(" % ["en", "de"][args.command == "decode"]), end="")
205219
elif args.command == "guess":

‎tests/test_common.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,21 @@ def test_handle_macros(self):
271271
self.assertIsNone(codext.add_macro("VALID-MACRO", "lzma", "base64"))
272272
self.assertIsNone(codext.remove("VALID-MACRO"))
273273
self.assertRaises(ValueError, codext.add_macro, "SHALL-FAIL", "base26", "sms", "letter-indices")
274-
274+
275+
def test_main_binary_output(self):
276+
import os, subprocess
277+
src = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "src")
278+
env = {**os.environ, 'PYTHONPATH': src + os.pathsep + os.environ.get('PYTHONPATH', "")}
279+
cli = lambda inp, *args: subprocess.run([sys.executable, "-m", "codext"] + list(args), input=inp, env=env,
280+
capture_output=True, check=True).stdout
281+
# binary output (not valid UTF-8) is written as is, not re-encoded as UTF-8 (the gzip header holding the time,
282+
# at bytes 4 to 7)
283+
skip = lambda codec, data: data[:4] + data[8:] if codec == "gzip" else data
284+
for codec in ["gzip", "piet"]:
285+
out = cli(b"test", "encode", codec)
286+
self.assertEqual(skip(codec, out), skip(codec, codext.encode(b"test", codec)))
287+
self.assertEqual(cli(out, "decode", codec), b"test")
288+
# text output is unchanged, including latin-1 text, printed as UTF-8
289+
self.assertEqual(cli(b"test", "encode", "base64"), b"dGVzdA==")
290+
self.assertEqual(cli("caf\u00e9".encode(), "encode", "reverse"), "\u00e9fac".encode())
291+
self.assertEqual(cli(b"test", "encode", "base100"), codext.encode(b"test", "base100"))

0 commit comments

Comments
 (0)