From 4660da949fa5834540abde37fabfbb7fed210dbe Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Fri, 17 Aug 2012 06:35:18 +0200 Subject: [PATCH] Fixed colorized output on Windows with Python 3. Closes #87. --- README.rst | 2 ++ httpie/core.py | 30 ++++++++++++++++++------------ httpie/models.py | 12 +++++++----- httpie/output.py | 17 +++++++++++++++++ tests/tests.py | 15 ++++++++++++--- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index a5d7c78d..a3d7b450 100644 --- a/README.rst +++ b/README.rst @@ -957,6 +957,8 @@ Changelog *You can click a version name to see a diff with the previous one.* * `0.2.8dev`_ + * Fixed colorized output on Windows with Python 3. + * Fixed installation on Windows with Python 3. * CRLF HTTP header field separation in the output. * Added exit status code ``2`` for timed-out requests. * Added ``--colors`` and ``--format`` in addition to ``--pretty``, to diff --git a/httpie/core.py b/httpie/core.py index 71b730e8..4499bd74 100644 --- a/httpie/core.py +++ b/httpie/core.py @@ -10,7 +10,6 @@ Invocation flow: 5. Exit. """ -from _socket import gaierror import sys import json import errno @@ -18,14 +17,14 @@ from pprint import pformat import requests import requests.auth -from requests.compat import str +from requests.compat import str, is_py3 from httpie import __version__ as httpie_version from requests import __version__ as requests_version from pygments import __version__ as pygments_version from .cli import parser from .models import Environment -from .output import output_stream, write +from .output import output_stream, write, write_with_colors_win_p3k from . import EXIT @@ -115,19 +114,20 @@ def main(args=sys.argv[1:], env=Environment()): status = EXIT.OK if debug: - sys.stderr.write('HTTPie version: %s\n' % httpie_version) - sys.stderr.write('Requests version: %s\n' % requests_version) - sys.stderr.write('Pygments version: %s\n' % pygments_version) + sys.stderr.write('HTTPie %s\n' % httpie_version) + sys.stderr.write('Requests %s\n' % requests_version) + sys.stderr.write('Pygments %s\n' % pygments_version) + sys.stderr.write('Python %s %s\n' % (sys.version, sys.platform)) try: args = parser.parse_args(args=args, env=env) - kwargs = get_requests_kwargs(args) + requests_kwargs = get_requests_kwargs(args) if args.debug: sys.stderr.write( - '\n>>> requests.request(%s)\n\n' % pformat(kwargs)) + '\n>>> requests.request(%s)\n\n' % pformat(requests_kwargs)) - response = requests.request(**kwargs) + response = requests.request(**requests_kwargs) if args.check_status: status = get_exist_status(response.status_code, @@ -137,10 +137,16 @@ def main(args=sys.argv[1:], env=Environment()): stream = output_stream(args, env, response.request, response) + write_kwargs = { + 'stream': stream, + 'outfile': env.stdout, + 'flush': env.stdout_isatty or args.stream + } try: - write(stream=stream, - outfile=env.stdout, - flush=env.stdout_isatty or args.stream) + if env.is_windows and is_py3 and 'colors' in args.prettify: + write_with_colors_win_p3k(**write_kwargs) + else: + write(**write_kwargs) except IOError as e: if not traceback and e.errno == errno.EPIPE: diff --git a/httpie/models.py b/httpie/models.py index 54db6c1d..b8b74e74 100644 --- a/httpie/models.py +++ b/httpie/models.py @@ -18,14 +18,16 @@ class Environment(object): if progname not in ['http', 'https']: progname = 'http' - if is_windows: - import colorama.initialise - colorama.initialise.init() - stdin_isatty = sys.stdin.isatty() stdin = sys.stdin stdout_isatty = sys.stdout.isatty() - stdout = sys.stdout + + if stdout_isatty and is_windows: + from colorama.initialise import wrap_stream + stdout = wrap_stream(sys.stdout, convert=None, + strip=None, autoreset=True, wrap=True) + else: + stdout = sys.stdout stderr = sys.stderr # Can be set to 0 to disable colors completely. diff --git a/httpie/output.py b/httpie/output.py index 1b6911ac..d5631059 100644 --- a/httpie/output.py +++ b/httpie/output.py @@ -61,6 +61,23 @@ def write(stream, outfile, flush): outfile.flush() +def write_with_colors_win_p3k(stream, outfile, flush): + """Like `write`, but colorized chunks are written as text + directly to `outfile` to ensure it gets processed by colorama. + Applies only to Windows with Python 3 and colorized terminal output. + + """ + color = b'\x1b[' + encoding = outfile.encoding + for chunk in stream: + if color in chunk: + outfile.write(chunk.decode(encoding)) + else: + outfile.buffer.write(chunk) + if flush: + outfile.flush() + + def output_stream(args, env, request, response): """Build and return a chain of iterators over the `request`-`response` exchange each of which yields `bytes` chunks. diff --git a/tests/tests.py b/tests/tests.py index 45a3cc4c..76ad3108 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -33,9 +33,9 @@ try: except ImportError: from urllib2 import urlopen try: - from unittest import skipIf + from unittest import skipIf, skip except ImportError: - + skip = lambda msg: lambda self: None def skipIf(cond, reason): def decorator(test_method): if cond: @@ -47,7 +47,6 @@ from requests import __version__ as requests_version from requests.compat import is_windows, is_py26, bytes, str - ################################################################# # Utils/setup ################################################################# @@ -863,6 +862,7 @@ class ExitStatusTest(BaseTestCase): self.assertEqual(r.exit_status, EXIT.OK) self.assertTrue(not r.stderr) + @skip('httpbin.org always returns 500') def test_timeout_exit_status(self): r = http( '--timeout=0.5', @@ -917,6 +917,15 @@ class ExitStatusTest(BaseTestCase): self.assertEqual(r.exit_status, EXIT.ERROR_HTTP_5XX) +class WindowsOnlyTests(BaseTestCase): + + @skip('FIXME: kills the runner') + #@skipIf(not is_windows, 'windows-only') + def test_windows_colorized_output(self): + # Spits out the colorized output. + http(httpbin('/get'), env=Environment()) + + class FakeWindowsTest(BaseTestCase): def test_stdout_redirect_not_supported_on_windows(self):