From 57fc606f6b4562b4f433b2f884d28ec2665a3b1b Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Fri, 20 Jul 2012 23:43:04 +0200 Subject: [PATCH] Changed default --print to "b" if stdout piped. If the output is piped to another program or redirected to a file, the new default behaviour is to only print the response body. (It can still be overriden via the ``--print`` flag.) --- README.rst | 7 +++++-- httpie/__main__.py | 5 ++++- httpie/cli.py | 13 +++++++------ httpie/cliparse.py | 29 ++++++++++++++++++++++------- tests/tests.py | 22 ++++++++++++++++++++-- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index f43f6f52..e33b29c4 100644 --- a/README.rst +++ b/README.rst @@ -143,13 +143,13 @@ case it will be used with no further processing:: That can be used for **piping services together**. The following example ``GET``-s JSON data from the Github API and ``POST``-s it to httpbin.org:: - http -b GET https://api.github.com/repos/jkbr/httpie | http POST httpbin.org/post + http GET https://api.github.com/repos/jkbr/httpie | http POST httpbin.org/post The above can be further simplified by omitting ``GET`` and ``POST`` because they are both default here. The first command has no request data, whereas the second one does via ``stdin``:: - http -b https://api.github.com/repos/jkbr/httpie | http httpbin.org/post + http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post An alternative to ``stdin`` is to pass a file name whose content will be used as the request body. It has the advantage that the ``Content-Type`` header @@ -284,6 +284,9 @@ Changelog --------- * `0.2.6dev `_ + * If the output is piped to another program or redirected to a file, + the new default behaviour is to only print the response body. + (It can still be overriden via the ``--print`` flag.) * Improved highlighing of HTTP headers. * Added query string parameters (param=:value). * Added support for terminal colors under Windows. diff --git a/httpie/__main__.py b/httpie/__main__.py index 36a4266e..a7f3948d 100644 --- a/httpie/__main__.py +++ b/httpie/__main__.py @@ -117,11 +117,14 @@ def main(args=None, stdin=sys.stdin, stdin_isatty=sys.stdin.isatty(), stdout=sys.stdout, stdout_isatty=sys.stdout.isatty()): parser = cli.parser + args = parser.parse_args( args=args if args is not None else sys.argv[1:], stdin=stdin, - stdin_isatty=stdin_isatty + stdin_isatty=stdin_isatty, + stdout_isatty=stdout_isatty, ) + response = _get_response(args) output = _get_output(args, stdout_isatty, response) output_bytes = output.encode('utf8') diff --git a/httpie/cli.py b/httpie/cli.py index b152f0ad..e93a5404 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -72,15 +72,16 @@ prettify.add_argument( output_options = parser.add_mutually_exclusive_group(required=False) output_options.add_argument('--print', '-p', dest='output_options', - default=cliparse.OUT_RESP_HEADERS + cliparse.OUT_RESP_BODY, help=_(''' - String specifying what should the output contain. - "{request_headers}" stands for the request headers and + String specifying what the output should contain: + "{request_headers}" stands for the request headers, and "{request_body}" for the request body. "{response_headers}" stands for the response headers and "{response_body}" for response the body. - Defaults to "hb" which means that the whole response - (headers and body) is printed. + The default behaviour is "hb" (i.e., the response + headers and body is printed), if standard output is not redirected. + If the output is piped to another program or to a file, + then only the body is printed by default. '''.format( request_headers=cliparse.OUT_REQ_HEADERS, request_body=cliparse.OUT_REQ_BODY, @@ -212,7 +213,7 @@ parser.add_argument( A key-value pair whose type is defined by the separator used. It can be an HTTP header (header:value), a data field to be used in the request body (field_name=value), - a raw JSON data field (field_name:=value), + a raw JSON data field (field_name:=value), a query parameter (name=:value), or a file field (field_name@/path/to/file). You can use a backslash to escape a colliding diff --git a/httpie/cliparse.py b/httpie/cliparse.py index 6ef26d84..aee788d8 100644 --- a/httpie/cliparse.py +++ b/httpie/cliparse.py @@ -52,11 +52,12 @@ class Parser(argparse.ArgumentParser): def parse_args(self, args=None, namespace=None, stdin=sys.stdin, - stdin_isatty=sys.stdin.isatty()): + stdin_isatty=sys.stdin.isatty(), + stdout_isatty=sys.stdout.isatty()): args = super(Parser, self).parse_args(args, namespace) - self._validate_output_options(args) + self._process_output_options(args, stdout_isatty) self._validate_auth_options(args) self._guess_method(args, stdin_isatty) self._parse_items(args) @@ -120,7 +121,8 @@ class Parser(argparse.ArgumentParser): def _parse_items(self, args): """ - Parse `args.items` into `args.headers`, `args.data`, `args.queries`, and `args.files`. + Parse `args.items` into `args.headers`, + `args.data`, `args.queries`, and `args.files`. """ args.headers = CaseInsensitiveDict() @@ -129,8 +131,11 @@ class Parser(argparse.ArgumentParser): args.files = OrderedDict() args.queries = CaseInsensitiveDict() try: - parse_items(items=args.items, headers=args.headers, - data=args.data, files=args.files, queries=args.queries) + parse_items(items=args.items, + headers=args.headers, + data=args.data, + files=args.files, + queries=args.queries) except ParseError as e: if args.traceback: raise @@ -157,7 +162,13 @@ class Parser(argparse.ArgumentParser): content_type = '%s; charset=%s' % (mime, encoding) args.headers['Content-Type'] = content_type - def _validate_output_options(self, args): + def _process_output_options(self, args, stdout_isatty): + if not args.output_options: + if stdout_isatty: + args.output_options = OUT_RESP_HEADERS + OUT_RESP_BODY + else: + args.output_options = OUT_RESP_BODY + unknown = set(args.output_options) - set(OUTPUT_OPTIONS) if unknown: self.error( @@ -270,7 +281,11 @@ class AuthCredentialsType(KeyValueType): def parse_items(items, data=None, headers=None, files=None, queries=None): - """Parse `KeyValueType` `items` into `data`, `headers`, `files`, and `queries`.""" + """ + Parse `KeyValueType` `items` into `data`, `headers`, `files`, + and `queries`. + + """ if headers is None: headers = {} if data is None: diff --git a/tests/tests.py b/tests/tests.py index ef3a5da0..2b24fed7 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -34,9 +34,19 @@ def http(*args, **kwargs): """ http_kwargs = { 'stdin_isatty': True, - 'stdout_isatty': False + 'stdout_isatty': True } http_kwargs.update(kwargs) + + command_line = ' '.join(args) + if ('stdout_isatty' not in kwargs + and '--pretty' not in command_line + and '--ugly' not in command_line): + # Make ugly default for testing purposes unless we're + # being explicit about it. It's so that we can test for + # strings in the response without having to always pass --ugly. + args = ['--ugly'] + list(args) + stdout = http_kwargs.setdefault('stdout', tempfile.TemporaryFile()) __main__.main(args=args, **http_kwargs) stdout.seek(0) @@ -168,6 +178,14 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase): self.assertIn('HTTP/1.1 200', r) self.assertIn('"Content-Type": "application/xml"', r) + def test_print_only_body_when_stdout_redirected_by_default(self): + r = http('GET', 'httpbin.org/get', stdout_isatty=False) + self.assertNotIn('HTTP/', r) + + def test_print_overridable_when_stdout_redirected(self): + r = http('--print=h', 'GET', 'httpbin.org/get', stdout_isatty=False) + self.assertIn('HTTP/1.1 200', r) + class ImplicitHTTPMethodTest(BaseTestCase): @@ -203,7 +221,7 @@ class PrettyFlagTest(BaseTestCase): r = http('GET', 'http://httpbin.org/get', stdout_isatty=True) self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r) - def test_pretty_enabled_by_default_unless_stdin_redirected(self): + def test_pretty_enabled_by_default_unless_stdout_redirected(self): r = http('GET', 'http://httpbin.org/get', stdout_isatty=False) self.assertNotIn(TERMINAL_COLOR_PRESENCE_CHECK, r)