summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Roztocil <jakub@roztocil.name>2012-07-24 01:09:14 +0200
committerJakub Roztocil <jakub@roztocil.name>2012-07-24 01:09:14 +0200
commit2646ebaaed81a53303c7fb60a6b50452c81bc025 (patch)
treef443eabeaa42e59cb2984ad1cd7957e5433af382
parentfba3912f2ee2495fcdc91d2c48723b2de6e4a8ab (diff)
Replaced --ignore-http-status with --check-status.
The default behaviour now is to exit with 0 on HTTP errors unless --check-status is set.
-rw-r--r--README.rst54
-rw-r--r--httpie/cli.py22
-rw-r--r--httpie/core.py18
-rw-r--r--httpie/models.py2
-rwxr-xr-xtests/tests.py93
5 files changed, 140 insertions, 49 deletions
diff --git a/README.rst b/README.rst
index 4068ebca..7430f1bd 100644
--- a/README.rst
+++ b/README.rst
@@ -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
diff --git a/httpie/cli.py b/httpie/cli.py
index 5c1b9610..fb77b8ca 100644
--- a/httpie/cli.py
+++ b/httpie/cli.py
@@ -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.
''')
)
diff --git a/httpie/core.py b/httpie/core.py
index 52711834..b2b7c121 100644
--- a/httpie/core.py
+++ b/httpie/core.py
@@ -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
diff --git a/httpie/models.py b/httpie/models.py
index 80fcec50..0aa2209d 100644
--- a/httpie/models.py
+++ b/httpie/models.py
@@ -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
diff --git a/tests/tests.py b/tests/tests.py
index eac1c7b0..ad397897 100755
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -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/')