Replaced --ignore-http-status with --check-status.

The default behaviour now is to exit with 0 on HTTP errors
unless --check-status is set.
This commit is contained in:
Jakub Roztocil 2012-07-24 01:09:14 +02:00
parent fba3912f2e
commit 2646ebaaed
5 changed files with 140 additions and 49 deletions

View File

@ -151,6 +151,11 @@ the second one does via ``stdin``::
http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
Note that when the output is redirected (like the examples above), HTTPie
applies a different set of defaults then for console output. Namely colors
aren't used (can be forced with ``--pretty``) and only the response body
gets printed (can be overwritten with ``--print``).
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
will automatically be set to the appropriate value based on the filename
@ -160,6 +165,25 @@ request will send the verbatim contents of the file with
http PUT httpbin.org/put @/data/file.xml
When using HTTPie from shell scripts you might want to use the
``--check-status`` flag. It instructs HTTPie to exit with an error if the
HTTP status is one of ``3xx``, ``4xx``, or ``5xx``. The exit status will
be ``3`` (unless ``--allow-redirects`` is set), ``4``, or ``5``
respectivelly::
#!/bin/bash
if http --check-status example.org/health &> /dev/null; then
echo 'OK!'
else
case $? in
3) echo 'Unexpected 3xx Redirection!' ;;
4) echo '4xx Client Error!' ;;
5) echo '5xx Server Error!' ;;
*) echo 'Other Error!' ;;
esac
fi
Flags
^^^^^
@ -170,7 +194,7 @@ See ``http -h`` for more details::
usage: http [-h] [--version] [--json | --form] [--traceback]
[--pretty | --ugly]
[--print OUTPUT_OPTIONS | --verbose | --headers | --body]
[--style STYLE] [--ignore-http-status] [--auth AUTH]
[--style STYLE] [--check-status] [--auth AUTH]
[--auth-type {basic,digest}] [--verify VERIFY] [--proxy PROXY]
[--allow-redirects] [--timeout TIMEOUT]
[METHOD] URL [ITEM [ITEM ...]]
@ -235,14 +259,16 @@ 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.
--check-status By default, HTTPie exits with 0 when no network or
other fatal errors occur. This flag instructs HTTPie
to also check the HTTP status code and exit with an
error if the status indicates one. 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 a 3xx (Redirect) and
--allow-redirects hasn't been set, then the exit
status is 3. Also an error message is written to
stderr if stdout is redirected.
--auth AUTH, -a AUTH username:password. If only the username is provided
(-a username), HTTPie will prompt for the password.
--auth-type {basic,digest}
@ -261,6 +287,7 @@ See ``http -h`` for more details::
socket.setdefaulttimeout() as fallback).
Contribute
-----------
@ -276,8 +303,13 @@ to see if the feature/bug has previously been discussed. Then fork
develop branch and submit a pull request. Note: Pull requests with tests and
documentation are 53.6% more awesome :)
Before a pull requests is submitted, it's a good idea to run the existing
suite of tests::
To point the ``http`` command to your working copy you can install HTTPie in
the editable mode::
pip install --editable .
It's a good idea to run the existing suite of tests before a pull requests is
submitted::
python setup.py test

View File

@ -126,17 +126,21 @@ parser.add_argument(
)
parser.add_argument(
'--ignore-http-status', default=False, action='store_true',
'--check-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 when no network or other fatal
errors occur.
This flag instructs HTTPie to also check the HTTP status code and
exit with an error if the status indicates one.
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 a 3xx (Redirect) and --allow-redirects
hasn't been set, then the exit status is 3.
Also an error message is written to stderr if stdout is redirected.
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.
''')
)

View File

@ -141,12 +141,20 @@ def main(args=sys.argv[1:], env=Environment()):
"""
args = cli.parser.parse_args(args=args, env=env)
response = get_response(args)
status = 0
if args.check_status:
status = get_exist_status(response.status_code,
args.allow_redirects)
if status and not env.stdout_isatty:
err = 'http error: %s %s\n' % (
response.raw.status, response.raw.reason)
env.stderr.write(err.encode('utf8'))
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)
return status

View File

@ -17,6 +17,8 @@ class Environment(object):
stdout_isatty = sys.stdout.isatty()
stdout = sys.stdout
stderr = sys.stderr
# Can be set to 0 to disable colors completely.
colors = 256 if '256color' in os.environ.get('TERM', '') else 88

View File

@ -53,10 +53,12 @@ def httpbin(path):
class Response(str):
"""
A unicode subclass holding the output of `main()` and the exit status.
A unicode subclass holding the output of `main()`, and also
the exit status and contents of ``stderr``.
"""
exit_status = None
stderr = None
def http(*args, **kwargs):
@ -75,17 +77,21 @@ def http(*args, **kwargs):
)
stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
stderr = kwargs['env'].stderr = tempfile.TemporaryFile()
exit_status = main(args=args, **kwargs)
stdout.seek(0)
stderr.seek(0)
response = Response(stdout.read().decode('utf8'))
response.exit_status = exit_status
r = Response(stdout.read().decode('utf8'))
r.stderr = stderr.read().decode('utf8')
r.exit_status = exit_status
stdout.close()
stderr.close()
return response
return r
class BaseTestCase(unittest.TestCase):
@ -155,6 +161,7 @@ class HTTPieTest(BaseTestCase):
env = Environment(
stdin=open(TEST_FILE_PATH),
stdin_isatty=False,
stdout_isatty=True,
colors=0,
)
@ -276,7 +283,10 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
r = http(
'GET',
httpbin('/get'),
env=Environment(stdout_isatty=False)
env=Environment(
stdin_isatty=True,
stdout_isatty=False
)
)
self.assertNotIn('HTTP/', r)
@ -286,7 +296,10 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
'--print=h',
'GET',
httpbin('/get'),
env=Environment(stdout_isatty=False)
env=Environment(
stdin_isatty=True,
stdout_isatty=False
)
)
self.assertIn('HTTP/1.1 200', r)
@ -326,6 +339,7 @@ class ImplicitHTTPMethodTest(BaseTestCase):
env = Environment(
stdin_isatty=False,
stdin=open(TEST_FILE_PATH),
stdout_isatty=True,
colors=0,
)
r = http(
@ -343,7 +357,10 @@ class PrettyFlagTest(BaseTestCase):
r = http(
'GET',
httpbin('/get'),
env=Environment(stdout_isatty=True),
env=Environment(
stdin_isatty=True,
stdout_isatty=True,
),
)
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
@ -359,7 +376,10 @@ class PrettyFlagTest(BaseTestCase):
'--pretty',
'GET',
httpbin('/get'),
env=Environment(stdout_isatty=False),
env=Environment(
stdin_isatty=True,
stdout_isatty=False
),
)
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
@ -508,16 +528,40 @@ class AuthTest(BaseTestCase):
class ExitStatusTest(BaseTestCase):
def test_3xx_exits_3(self):
def test_ok_response_exits_0(self):
r = http(
'GET',
httpbin('/status/301')
httpbin('/status/200')
)
self.assertIn('HTTP/1.1 200', r)
self.assertEqual(r.exit_status, 0)
def test_error_response_exits_0_without_check_status(self):
r = http(
'GET',
httpbin('/status/500')
)
self.assertIn('HTTP/1.1 500', r)
self.assertEqual(r.exit_status, 0)
def test_3xx_check_status_exits_3_and_stderr_when_stdout_redirected(self):
r = http(
'--check-status',
'--headers', # non-terminal, force headers
'GET',
httpbin('/status/301'),
env=Environment(
stdout_isatty=False,
stdin_isatty=True,
)
)
self.assertIn('HTTP/1.1 301', r)
self.assertEqual(r.exit_status, 3)
self.assertIn('301 moved permanently', r.stderr.lower())
def test_3xx_redirects_allowed_exits_0(self):
def test_3xx_check_status_redirects_allowed_exits_0(self):
r = http(
'--check-status',
'--allow-redirects',
'GET',
httpbin('/status/301')
@ -526,31 +570,26 @@ class ExitStatusTest(BaseTestCase):
self.assertIn('HTTP/1.1 200 OK', r)
self.assertEqual(r.exit_status, 0)
def test_4xx_exits_4(self):
def test_4xx_check_status_exits_4(self):
r = http(
'--check-status',
'GET',
httpbin('/status/401')
)
self.assertIn('HTTP/1.1 401', r)
self.assertEqual(r.exit_status, 4)
# Also stderr should be empty since stdout isn't redirected.
self.assert_(not r.stderr)
def test_5xx_exits_5(self):
def test_5xx_check_status_exits_5(self):
r = http(
'--check-status',
'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.
@ -660,7 +699,10 @@ class ArgumentParserTestCase(unittest.TestCase):
args.url = 'http://example.com/'
args.items = []
self.parser._guess_method(args, Environment())
self.parser._guess_method(args, Environment(
stdin_isatty=True,
stdout_isatty=True,
))
self.assertEquals(args.method, 'GET')
self.assertEquals(args.url, 'http://example.com/')
@ -687,7 +729,10 @@ class ArgumentParserTestCase(unittest.TestCase):
args.url = 'test:header'
args.items = []
self.parser._guess_method(args, Environment())
self.parser._guess_method(args, Environment(
stdin_isatty=True,
stdout_isatty=True,
))
self.assertEquals(args.method, 'GET')
self.assertEquals(args.url, 'http://example.com/')