httpie-cli/httpie/core.py

203 lines
5.9 KiB
Python
Raw Normal View History

2012-07-26 06:37:03 +02:00
"""This module provides the main functionality of HTTPie.
Invocation flow:
1. Read, validate and process the input (args, `stdin`).
2. Create and send a request.
3. Stream, and possibly process and format, the parts
of the request-response exchange selected by output options.
4. Simultaneously write to `stdout`
5. Exit.
2012-07-26 06:37:03 +02:00
"""
import sys
import errno
2012-07-26 00:26:23 +02:00
import requests
2012-08-07 14:50:51 +02:00
from requests import __version__ as requests_version
from pygments import __version__ as pygments_version
2012-07-26 00:26:23 +02:00
from httpie import __version__ as httpie_version, ExitStatus
from httpie.compat import str, bytes, is_py3
from httpie.client import get_response
from httpie.downloads import Download
from httpie.context import Environment
from httpie.plugins import plugin_manager
from httpie.output.streams import (
build_output_stream,
write, write_with_colors_win_py3
)
2012-12-11 12:54:34 +01:00
def get_exit_status(http_status, follow=False):
2012-12-05 05:03:18 +01:00
"""Translate HTTP status code to exit status code."""
2012-12-11 12:54:34 +01:00
if 300 <= http_status <= 399 and not follow:
# Redirect
2012-12-05 05:03:18 +01:00
return ExitStatus.ERROR_HTTP_3XX
2012-12-11 12:54:34 +01:00
elif 400 <= http_status <= 499:
# Client Error
2012-12-05 05:03:18 +01:00
return ExitStatus.ERROR_HTTP_4XX
2012-12-11 12:54:34 +01:00
elif 500 <= http_status <= 599:
# Server Error
2012-12-05 05:03:18 +01:00
return ExitStatus.ERROR_HTTP_5XX
else:
2012-12-05 05:03:18 +01:00
return ExitStatus.OK
def print_debug_info(env):
2014-04-24 18:32:15 +02:00
env.stderr.writelines([
'HTTPie %s\n' % httpie_version,
'HTTPie data: %s\n' % env.config.directory,
'Requests %s\n' % requests_version,
'Pygments %s\n' % pygments_version,
'Python %s %s\n' % (sys.version, sys.platform)
])
def decode_args(args, stdin_encoding):
"""
Convert all bytes ags to str
by decoding them using stdin encoding.
"""
return [
2014-06-03 19:44:22 +02:00
arg.decode(stdin_encoding)
if type(arg) == bytes else arg
for arg in args
]
def main(args=sys.argv[1:], env=Environment(), error=None):
2012-07-26 06:37:03 +02:00
"""Run the main program and write the output to ``env.stdout``.
2012-12-05 05:03:18 +01:00
Return exit status code.
"""
2014-06-03 19:44:22 +02:00
args = decode_args(args, env.stdin_encoding)
2013-09-21 23:46:15 +02:00
plugin_manager.load_installed_plugins()
2014-06-03 19:44:22 +02:00
from httpie.cli import parser
2012-09-17 02:15:00 +02:00
if env.config.default_options:
args = env.config.default_options + args
def _error(msg, *args, **kwargs):
msg = msg % args
level = kwargs.get('level', 'error')
2013-04-11 21:23:15 +02:00
env.stderr.write('\nhttp: %s: %s\n' % (level, msg))
if error is None:
error = _error
debug = '--debug' in args
2012-08-07 14:50:51 +02:00
traceback = debug or '--traceback' in args
2012-12-11 12:54:34 +01:00
exit_status = ExitStatus.OK
2012-08-07 14:50:51 +02:00
if debug:
print_debug_info(env)
if args == ['--debug']:
2012-12-11 12:54:34 +01:00
return exit_status
2012-08-07 14:50:51 +02:00
download = None
try:
args = parser.parse_args(args=args, env=env)
2012-09-17 02:15:00 +02:00
if args.download:
2013-03-24 15:23:18 +01:00
args.follow = True # --download implies --follow.
download = Download(
output_file=args.output_file,
progress_file=env.stderr,
resume=args.download_resume
)
2013-03-24 15:23:18 +01:00
download.pre_request(args.headers)
2012-09-17 02:15:00 +02:00
response = get_response(args, config_dir=env.config.directory)
2013-03-24 15:23:18 +01:00
if args.check_status or download:
exit_status = get_exit_status(
http_status=response.status_code,
follow=args.follow
)
2012-12-11 12:54:34 +01:00
if not env.stdout_isatty and exit_status != ExitStatus.OK:
error('HTTP %s %s',
response.raw.status,
response.raw.reason,
level='warning')
write_kwargs = {
'stream': build_output_stream(
args, env, response.request, response),
# This will in fact be `stderr` with `--download`
'outfile': env.stdout,
'flush': env.stdout_isatty or args.stream
}
try:
if env.is_windows and is_py3 and 'colors' in args.prettify:
write_with_colors_win_py3(**write_kwargs)
else:
write(**write_kwargs)
2013-03-24 15:23:18 +01:00
if download and exit_status == ExitStatus.OK:
# Response body download.
download_stream, download_to = download.start(response)
write(
stream=download_stream,
outfile=download_to,
flush=False,
)
2013-03-24 15:23:18 +01:00
download.finish()
if download.interrupted:
exit_status = ExitStatus.ERROR
error('Incomplete download: size=%d; downloaded=%d' % (
download.status.total_size,
download.status.downloaded
))
except IOError as e:
2012-08-07 14:50:51 +02:00
if not traceback and e.errno == errno.EPIPE:
# Ignore broken pipes unless --traceback.
env.stderr.write('\n')
else:
raise
except KeyboardInterrupt:
2012-08-07 14:50:51 +02:00
if traceback:
raise
env.stderr.write('\n')
2012-12-11 12:54:34 +01:00
exit_status = ExitStatus.ERROR
except SystemExit as e:
if e.code != ExitStatus.OK:
if traceback:
raise
env.stderr.write('\n')
exit_status = ExitStatus.ERROR
except requests.Timeout:
2012-12-11 12:54:34 +01:00
exit_status = ExitStatus.ERROR_TIMEOUT
error('Request timed out (%ss).', args.timeout)
2012-12-05 05:03:18 +01:00
except Exception as e:
2012-12-05 05:03:18 +01:00
# TODO: Better distinction between expected and unexpected errors.
# Network errors vs. bugs, etc.
2012-08-07 14:50:51 +02:00
if traceback:
raise
msg = str(e)
if hasattr(e, 'request'):
request = e.request
if hasattr(request, 'url'):
msg += ' while doing %s request to URL: %s' % (
request.method, request.url)
error('%s: %s', type(e).__name__, msg)
2012-12-11 12:54:34 +01:00
exit_status = ExitStatus.ERROR
finally:
if download and not download.finished:
download.failed()
2012-12-11 12:54:34 +01:00
return exit_status