summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Roztocil <jakub@roztocil.name>2012-07-23 19:35:44 +0200
committerJakub Roztocil <jakub@roztocil.name>2012-07-23 19:40:50 +0200
commit0572158ba1dd26d58e148e694dba5c3aaac9532e (patch)
tree6b59944615348762198ed2e2857bafff0868eb85
parent0a673613eff75a276981274ea7f60ba232d9a61c (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.rst15
-rw-r--r--httpie/__main__.py3
-rw-r--r--httpie/cli.py15
-rw-r--r--httpie/core.py29
-rw-r--r--httpie/output.py1
-rw-r--r--tests/tests.py73
6 files changed, 123 insertions, 13 deletions
diff --git a/README.rst b/README.rst
index 9505e97f..4068ebca 100644
--- a/README.rst
+++ b/README.rst
@@ -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.
#################################################################