diff options
author | Jakub Roztocil <jakub@roztocil.name> | 2012-07-23 19:35:44 +0200 |
---|---|---|
committer | Jakub Roztocil <jakub@roztocil.name> | 2012-07-23 19:40:50 +0200 |
commit | 0572158ba1dd26d58e148e694dba5c3aaac9532e (patch) | |
tree | 6b59944615348762198ed2e2857bafff0868eb85 | |
parent | 0a673613eff75a276981274ea7f60ba232d9a61c (diff) |
Added exit codes for HTTP 3xx, 4xx, 5xx (3, 4, 5).
Also added `--ignore-http-status` to ensure 0 exit status.
HTTP 3xx result in 0 exit status when `--allow-redirects` is set.
-rw-r--r-- | README.rst | 15 | ||||
-rw-r--r-- | httpie/__main__.py | 3 | ||||
-rw-r--r-- | httpie/cli.py | 15 | ||||
-rw-r--r-- | httpie/core.py | 29 | ||||
-rw-r--r-- | httpie/output.py | 1 | ||||
-rw-r--r-- | tests/tests.py | 73 |
6 files changed, 123 insertions, 13 deletions
@@ -170,9 +170,9 @@ See ``http -h`` for more details:: usage: http [-h] [--version] [--json | --form] [--traceback] [--pretty | --ugly] [--print OUTPUT_OPTIONS | --verbose | --headers | --body] - [--style STYLE] [--auth AUTH] [--auth-type {basic,digest}] - [--verify VERIFY] [--proxy PROXY] [--allow-redirects] - [--timeout TIMEOUT] + [--style STYLE] [--ignore-http-status] [--auth AUTH] + [--auth-type {basic,digest}] [--verify VERIFY] [--proxy PROXY] + [--allow-redirects] [--timeout TIMEOUT] [METHOD] URL [ITEM [ITEM ...]] HTTPie - cURL for humans. <http://httpie.org> @@ -235,6 +235,14 @@ See ``http -h`` for more details:: make sure that the $TERM environment variable is set to "xterm-256color" or similar (e.g., via `export TERM =xterm-256color' in your ~/.bashrc). + --ignore-http-status This flag tells HTTP to ignore the HTTP status code + and exit with 0. By default, HTTPie exits with 0 only + if no errors occur and the request is successful. When + the server replies with a 4xx (Client Error) or 5xx + (Server Error) status code, HTTPie exits with 4 or 5 + respectively. If the response is 3xx (Redirect) and + --allow-redirects isn't set, then the exit status is + 3. --auth AUTH, -a AUTH username:password. If only the username is provided (-a username), HTTPie will prompt for the password. --auth-type {basic,digest} @@ -287,6 +295,7 @@ Changelog --------- * `0.2.6dev <https://github.com/jkbr/httpie/compare/0.2.5...master>`_ + * Added exit status codes for HTTP 3xx, 4xx and 5xx (3, 4, 5). * 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.) diff --git a/httpie/__main__.py b/httpie/__main__.py index 83a66d6c..cbda8afd 100644 --- a/httpie/__main__.py +++ b/httpie/__main__.py @@ -3,8 +3,9 @@ The main entry point. Invoke as `http' or `python -m httpie'. """ +import sys from .core import main if __name__ == '__main__': - main() + sys.exit(main()) diff --git a/httpie/cli.py b/httpie/cli.py index fa166b63..5c1b9610 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -125,6 +125,21 @@ parser.add_argument( ''') % ', '.join(sorted(AVAILABLE_STYLES)) ) +parser.add_argument( + '--ignore-http-status', default=False, action='store_true', + help=_(''' + This flag tells HTTP to ignore the HTTP status code + and exit with 0. + + By default, HTTPie exits with 0 only if no errors occur + and the request is successful. When the server + replies with a 4xx (Client Error) or 5xx (Server Error) + status code, HTTPie exits with 4 or 5 respectively. + If the response is 3xx (Redirect) and --allow-redirects + isn't set, then the exit status is 3. + ''') +) + # ``requests.request`` keyword arguments. parser.add_argument( '--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON), diff --git a/httpie/core.py b/httpie/core.py index f0617e31..52711834 100644 --- a/httpie/core.py +++ b/httpie/core.py @@ -114,10 +114,39 @@ def get_output(args, env, response): return ''.join(buf) +def get_exist_status(code, allow_redirects=False): + """ + Translate HTTP status code to exit status. + + """ + if 300 <= code <= 399 and not allow_redirects: + # Redirect + return 3 + elif 400 <= code <= 499: + # Client Error + return 4 + elif 500 <= code <= 599: + # Server Error + return 5 + else: + return 0 + + def main(args=sys.argv[1:], env=Environment()): + """ + Run the main program and write the output to ``env.stdout``. + + Return exit status. + + """ args = cli.parser.parse_args(args=args, env=env) response = get_response(args) output = get_output(args, env, response) output_bytes = output.encode('utf8') f = getattr(env.stdout, 'buffer', env.stdout) f.write(output_bytes) + if args.ignore_http_status: + return 0 + else: + return get_exist_status( + response.status_code, args.allow_redirects) diff --git a/httpie/output.py b/httpie/output.py index 1545e0af..226a3b0d 100644 --- a/httpie/output.py +++ b/httpie/output.py @@ -2,7 +2,6 @@ Colorizing of HTTP messages and content processing. """ -import os import re import json import pygments diff --git a/tests/tests.py b/tests/tests.py index 0c89fd6d..4d92b690 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -21,8 +21,7 @@ import json import tempfile import unittest import argparse -from requests import Response -from requests.compat import is_py26, is_py3 +from requests.compat import is_py26, is_py3, str ################################################################# @@ -51,28 +50,40 @@ def httpbin(path): return HTTPBIN_URL + path +class Response(str): + """ + A unicode subclass holding the output of `main()` and the exit status. + + """ + exit_status = None + + def http(*args, **kwargs): """ - Invoke `httpie.__main__.main` with `args` and `kwargs`, + Invoke `httpie.core.main()` with `args` and `kwargs`, and return a unicode response. """ if 'env' not in kwargs: - # Ensure that we have terminal by default - # (needed for Travis). + # Ensure that we have terminal by default (needed for Travis). kwargs['env'] = Environment( colors=0, stdin_isatty=True, stdout_isatty=True, ) - stdout = kwargs['env'].stdout = tempfile.TemporaryFile() - main(args=args, **kwargs) + + exit_status = main(args=args, **kwargs) + stdout.seek(0) - response = stdout.read().decode('utf8') + + response = Response(stdout.read().decode('utf8')) + response.exit_status = exit_status + stdout.close() + return response @@ -494,6 +505,52 @@ class AuthTest(BaseTestCase): self.assertIn('"user": "user"', r) +class ExitStatusTest(BaseTestCase): + + def test_3xx_exits_3(self): + r = http( + 'GET', + httpbin('/status/301') + ) + self.assertIn('HTTP/1.1 301', r) + self.assertEqual(r.exit_status, 3) + + def test_3xx_redirects_allowed_exits_0(self): + r = http( + '--allow-redirects', + 'GET', + httpbin('/status/301') + ) + # The redirect will be followed so 200 is expected. + self.assertIn('HTTP/1.1 200 OK', r) + self.assertEqual(r.exit_status, 0) + + def test_4xx_exits_4(self): + r = http( + 'GET', + httpbin('/status/401') + ) + self.assertIn('HTTP/1.1 401', r) + self.assertEqual(r.exit_status, 4) + + def test_5xx_exits_5(self): + r = http( + 'GET', + httpbin('/status/500') + ) + self.assertIn('HTTP/1.1 500', r) + self.assertEqual(r.exit_status, 5) + + def test_ignore_http_status_exits_0(self): + r = http( + '--ignore-http-status', + 'GET', + httpbin('/status/500') + ) + self.assertIn('HTTP/1.1 500', r) + self.assertEqual(r.exit_status, 0) + + ################################################################# # CLI argument parsing related tests. ################################################################# |