From 01ca7f0eb28790a594ce94b337caf6f776078215 Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Tue, 1 Mar 2016 20:24:50 +0800 Subject: [PATCH] Ignore redirected stdout with --output, -o This makes it easier to use HTTPie in cron jobs and scripts. Closes #259 --- CHANGELOG.rst | 5 ++++- Makefile | 2 +- httpie/cli.py | 5 +++-- httpie/core.py | 4 ++++ httpie/input.py | 6 ++---- tests/test_output.py | 20 ++++++++++++++++++++ tests/test_windows.py | 4 +++- 7 files changed, 37 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 64ec5660..4fde5aaf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ Change Log This document records all notable changes to `HTTPie `_. This project adheres to `Semantic Versioning `_. + `1.0.0-dev`_ (Unreleased) ------------------------- @@ -15,6 +16,8 @@ This project adheres to `Semantic Versioning `_. * Added ``-F`` as short name for ``--follow`` * Added JSON detection when ``--json, -j`` is used in order to correctly format JSON responses even when an incorrect ``Content-Type`` is returned. +* Redirected ``stdout`` doesn't trigger an error anymore when ``--output FILE`` + is set. * Changed the default color style back to ``solarized`` as it supports both the light and dark terminal background mode * Fixed ``--session`` when used with ``--download`` @@ -22,7 +25,7 @@ This project adheres to `Semantic Versioning `_. `0.9.3`_ (2016-01-01) -------------------------- +--------------------- * Changed the default color ``--style`` from ``solarized`` to ``monokai`` * Added basic Bash autocomplete support (need to be installed manually) diff --git a/Makefile b/Makefile index dadf05cc..89e0342e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ END=" \#\#\# \033[0m\n" all: test -init: +init: uninstall-httpie @echo $(TAG)Installing dev requirements$(END) pip install --upgrade -r $(REQUIREMENTS) diff --git a/httpie/cli.py b/httpie/cli.py index f03bb46b..e893af25 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -310,8 +310,9 @@ output_options.add_argument( dest='output_file', metavar='FILE', help=""" - Save output to FILE. If --download is set, then only the response body is - saved to the file. Other parts of the HTTP exchange are printed to stderr. + Save output to FILE instead of stdout. If --download is also set, then only + the response body is saved to FILE. Other parts of the HTTP exchange are + printed to stderr. """ diff --git a/httpie/core.py b/httpie/core.py index e6875524..2079a914 100644 --- a/httpie/core.py +++ b/httpie/core.py @@ -204,4 +204,8 @@ def main(args=sys.argv[1:], env=Environment(), error=None): if downloader and not downloader.finished: downloader.failed() + if (not isinstance(args, list) and args.output_file and + args.output_file_specified): + args.output_file.close() + return exit_status diff --git a/httpie/input.py b/httpie/input.py index 53810978..232e2d9e 100644 --- a/httpie/input.py +++ b/httpie/input.py @@ -171,12 +171,10 @@ class HTTPieArgumentParser(ArgumentParser): Modify `env.stdout` and `env.stdout_isatty` based on args, if needed. """ - if not self.env.stdout_isatty and self.args.output_file: - self.error('Cannot use --output, -o with redirected output.') - + self.args.output_file_specified = bool(self.args.output_file) if self.args.download: # FIXME: Come up with a cleaner solution. - if not self.env.stdout_isatty: + if not self.args.output_file and not self.env.stdout_isatty: # Use stdout as the download output file. self.args.output_file = self.env.stdout # With `--download`, we write everything that would normally go to diff --git a/tests/test_output.py b/tests/test_output.py index 483eeb39..ac58315f 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -1,10 +1,30 @@ +import os +from tempfile import gettempdir + import pytest from utils import TestEnvironment, http, HTTP_OK, COLOR, CRLF from httpie import ExitStatus +from httpie.compat import urlopen from httpie.output.formatters.colors import get_lexer +@pytest.mark.parametrize('stdout_isatty', [(True,), (False,)]) +def test_output_option(httpbin, stdout_isatty): + output_filename = os.path.join(gettempdir(), test_output_option.__name__) + url = httpbin + '/robots.txt' + + r = http('--output', output_filename, url, + env=TestEnvironment(stdout_isatty=stdout_isatty)) + assert r == '' + + expected_body = urlopen(url).read().decode() + with open(output_filename, 'r') as f: + actual_body = f.read() + + assert actual_body == expected_body + + class TestVerboseFlag: def test_verbose(self, httpbin): r = http('--verbose', diff --git a/tests/test_windows.py b/tests/test_windows.py index 91669f5c..ca5e2bb6 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -22,7 +22,9 @@ class TestFakeWindows: def test_output_file_pretty_not_allowed_on_windows(self, httpbin): env = TestEnvironment(is_windows=True) output_file = os.path.join( - tempfile.gettempdir(), '__httpie_test_output__') + tempfile.gettempdir(), + self.test_output_file_pretty_not_allowed_on_windows.__name__ + ) r = http('--output', output_file, '--pretty=all', 'GET', httpbin.url + '/get', env=env, error_exit_ok=True)