mirror of
https://github.com/httpie/cli.git
synced 2025-02-18 02:20:51 +01:00
Implemented more robust unicode handling.
* Immediatelly convert all args from `bytes` to `str`. * Added `Environment.stdin_encoding` and `Environment.stdout_encoding` * Allow unicode characters in HTTP headers and basic auth credentials by encoding them using UTF8 instead of latin1 (#212).
This commit is contained in:
parent
5c29a4e551
commit
15e62ad26d
@ -6,6 +6,7 @@ import requests
|
|||||||
|
|
||||||
from . import sessions
|
from . import sessions
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
from .compat import str
|
||||||
from .plugins import plugin_manager
|
from .plugins import plugin_manager
|
||||||
|
|
||||||
|
|
||||||
@ -79,11 +80,18 @@ def get_requests_kwargs(args):
|
|||||||
if args.certkey:
|
if args.certkey:
|
||||||
cert = (cert, args.certkey)
|
cert = (cert, args.certkey)
|
||||||
|
|
||||||
|
# This allows for unicode headers which is non-standard but practical.
|
||||||
|
# See: https://github.com/jkbr/httpie/issues/212
|
||||||
|
headers = dict(
|
||||||
|
(k.encode('utf8'), v.encode('utf8') if isinstance(v, str) else v)
|
||||||
|
for k, v in args.headers.items()
|
||||||
|
)
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'stream': True,
|
'stream': True,
|
||||||
'method': args.method.lower(),
|
'method': args.method.lower(),
|
||||||
'url': args.url,
|
'url': args.url,
|
||||||
'headers': args.headers,
|
'headers': headers,
|
||||||
'data': args.data,
|
'data': args.data,
|
||||||
'verify': {
|
'verify': {
|
||||||
'yes': True,
|
'yes': True,
|
||||||
|
@ -18,7 +18,7 @@ from httpie import __version__ as httpie_version
|
|||||||
from requests import __version__ as requests_version
|
from requests import __version__ as requests_version
|
||||||
from pygments import __version__ as pygments_version
|
from pygments import __version__ as pygments_version
|
||||||
|
|
||||||
from .compat import str, is_py3
|
from .compat import str, bytes, is_py3
|
||||||
from .client import get_response
|
from .client import get_response
|
||||||
from .downloads import Download
|
from .downloads import Download
|
||||||
from .models import Environment
|
from .models import Environment
|
||||||
@ -52,12 +52,26 @@ def print_debug_info(env):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def decode_args(args, stdin_encoding):
|
||||||
|
"""
|
||||||
|
Convert all bytes ags to str
|
||||||
|
by decoding them using stdin encoding.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
arg.decode(stdin_encoding) if type(arg) == bytes else arg
|
||||||
|
for arg in args
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv[1:], env=Environment()):
|
def main(args=sys.argv[1:], env=Environment()):
|
||||||
"""Run the main program and write the output to ``env.stdout``.
|
"""Run the main program and write the output to ``env.stdout``.
|
||||||
|
|
||||||
Return exit status code.
|
Return exit status code.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
args = decode_args(args, env.stdin_encoding)
|
||||||
|
|
||||||
plugin_manager.load_installed_plugins()
|
plugin_manager.load_installed_plugins()
|
||||||
from .cli import parser
|
from .cli import parser
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ from .compat import OrderedDict
|
|||||||
# https://github.com/jkbr/httpie/issues/130
|
# https://github.com/jkbr/httpie/issues/130
|
||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
|
||||||
from .compat import urlsplit, str
|
from .compat import urlsplit, str, bytes
|
||||||
from .sessions import VALID_SESSION_NAME_PATTERN
|
from .sessions import VALID_SESSION_NAME_PATTERN
|
||||||
|
|
||||||
|
|
||||||
@ -153,14 +153,13 @@ class Parser(ArgumentParser):
|
|||||||
# noinspection PyShadowingBuiltins
|
# noinspection PyShadowingBuiltins
|
||||||
def _print_message(self, message, file=None):
|
def _print_message(self, message, file=None):
|
||||||
# Sneak in our stderr/stdout.
|
# Sneak in our stderr/stdout.
|
||||||
print(file)
|
|
||||||
file = {
|
file = {
|
||||||
sys.stdout: self.env.stdout,
|
sys.stdout: self.env.stdout,
|
||||||
sys.stderr: self.env.stderr,
|
sys.stderr: self.env.stderr,
|
||||||
None: self.env.stderr
|
None: self.env.stderr
|
||||||
}.get(file, file)
|
}.get(file, file)
|
||||||
if not hasattr(file, 'buffer'):
|
if not hasattr(file, 'buffer') and isinstance(message, str):
|
||||||
message = message.encode('utf8')
|
message = message.encode(self.env.stdout_encoding)
|
||||||
super(Parser, self)._print_message(message, file)
|
super(Parser, self)._print_message(message, file)
|
||||||
|
|
||||||
def _setup_standard_streams(self):
|
def _setup_standard_streams(self):
|
||||||
@ -502,7 +501,7 @@ class KeyValueArgType(object):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
raise ArgumentTypeError(
|
raise ArgumentTypeError(
|
||||||
'"%s" is not a valid value' % string)
|
u'"%s" is not a valid value' % string)
|
||||||
|
|
||||||
return self.key_value_class(
|
return self.key_value_class(
|
||||||
key=key, value=value, sep=sep, orig=string)
|
key=key, value=value, sep=sep, orig=string)
|
||||||
|
@ -40,11 +40,27 @@ class Environment(object):
|
|||||||
stdout = sys.stdout
|
stdout = sys.stdout
|
||||||
stderr = sys.stderr
|
stderr = sys.stderr
|
||||||
|
|
||||||
|
stdin_encoding = None
|
||||||
|
stdout_encoding = None
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
assert all(hasattr(type(self), attr)
|
assert all(hasattr(type(self), attr)
|
||||||
for attr in kwargs.keys())
|
for attr in kwargs.keys())
|
||||||
self.__dict__.update(**kwargs)
|
self.__dict__.update(**kwargs)
|
||||||
|
|
||||||
|
if self.stdin_encoding is None:
|
||||||
|
self.stdin_encoding = getattr(
|
||||||
|
self.stdin, 'encoding', None) or 'utf8'
|
||||||
|
|
||||||
|
if self.stdout_encoding is None:
|
||||||
|
actual_stdout = self.stdout
|
||||||
|
if is_windows:
|
||||||
|
from colorama import AnsiToWin32
|
||||||
|
if isinstance(AnsiToWin32, self.stdout):
|
||||||
|
actual_stdout = self.stdout.wrapped
|
||||||
|
self.stdout_encoding = getattr(
|
||||||
|
actual_stdout, 'encoding', None) or 'utf8'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def config(self):
|
def config(self):
|
||||||
if not hasattr(self, '_config'):
|
if not hasattr(self, '_config'):
|
||||||
|
@ -167,7 +167,7 @@ class BaseStream(object):
|
|||||||
|
|
||||||
def _get_headers(self):
|
def _get_headers(self):
|
||||||
"""Return the headers' bytes."""
|
"""Return the headers' bytes."""
|
||||||
return self.msg.headers.encode('ascii')
|
return self.msg.headers.encode('utf8')
|
||||||
|
|
||||||
def _iter_body(self):
|
def _iter_body(self):
|
||||||
"""Return an iterator over the message body."""
|
"""Return an iterator over the message body."""
|
||||||
@ -221,7 +221,7 @@ class EncodedStream(BaseStream):
|
|||||||
|
|
||||||
if env.stdout_isatty:
|
if env.stdout_isatty:
|
||||||
# Use the encoding supported by the terminal.
|
# Use the encoding supported by the terminal.
|
||||||
output_encoding = getattr(env.stdout, 'encoding', None)
|
output_encoding = env.stdout_encoding
|
||||||
else:
|
else:
|
||||||
# Preserve the message encoding.
|
# Preserve the message encoding.
|
||||||
output_encoding = self.msg.encoding
|
output_encoding = self.msg.encoding
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from base64 import b64encode
|
||||||
|
|
||||||
import requests.auth
|
import requests.auth
|
||||||
|
|
||||||
from .base import AuthPlugin
|
from .base import AuthPlugin
|
||||||
@ -8,13 +10,28 @@ class BuiltinAuthPlugin(AuthPlugin):
|
|||||||
package_name = '(builtin)'
|
package_name = '(builtin)'
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPBasicAuth(requests.auth.HTTPBasicAuth):
|
||||||
|
|
||||||
|
def __call__(self, r):
|
||||||
|
"""
|
||||||
|
Override username/password serialization to allow unicode.
|
||||||
|
|
||||||
|
See https://github.com/jkbr/httpie/issues/212
|
||||||
|
|
||||||
|
"""
|
||||||
|
credentials = u'%s:%s' % (self.username, self.password)
|
||||||
|
token = b64encode(credentials.encode('utf8')).strip()
|
||||||
|
r.headers['Authorization'] = 'Basic %s' % token
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
class BasicAuthPlugin(BuiltinAuthPlugin):
|
class BasicAuthPlugin(BuiltinAuthPlugin):
|
||||||
|
|
||||||
name = 'Basic HTTP auth'
|
name = 'Basic HTTP auth'
|
||||||
auth_type = 'basic'
|
auth_type = 'basic'
|
||||||
|
|
||||||
def get_auth(self, username, password):
|
def get_auth(self, username, password):
|
||||||
return requests.auth.HTTPBasicAuth(username, password)
|
return HTTPBasicAuth(username, password)
|
||||||
|
|
||||||
|
|
||||||
class DigestAuthPlugin(BuiltinAuthPlugin):
|
class DigestAuthPlugin(BuiltinAuthPlugin):
|
||||||
|
@ -94,7 +94,12 @@ def http(*args, **kwargs):
|
|||||||
`exit_status`: the exit status
|
`exit_status`: the exit status
|
||||||
`json`: decoded JSON (if possible) or `None`
|
`json`: decoded JSON (if possible) or `None`
|
||||||
|
|
||||||
Exceptions are propagated except for SystemExit.
|
Exceptions are propagated.
|
||||||
|
|
||||||
|
If you pass ``error_exit_ok=True``, then error exit statuses
|
||||||
|
won't result into an exception.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
$ http --auth=user:password GET httpbin.org/basic-auth/user/password
|
$ http --auth=user:password GET httpbin.org/basic-auth/user/password
|
||||||
|
|
||||||
@ -112,6 +117,7 @@ def http(*args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
error_exit_ok = kwargs.pop('error_exit_ok', False)
|
||||||
env = kwargs.get('env')
|
env = kwargs.get('env')
|
||||||
if not env:
|
if not env:
|
||||||
env = kwargs['env'] = TestEnvironment()
|
env = kwargs['env'] = TestEnvironment()
|
||||||
@ -123,21 +129,35 @@ def http(*args, **kwargs):
|
|||||||
if '--debug' not in args and '--traceback' not in args:
|
if '--debug' not in args and '--traceback' not in args:
|
||||||
args = ['--traceback'] + args
|
args = ['--traceback'] + args
|
||||||
|
|
||||||
|
def dump_stderr():
|
||||||
|
stderr.seek(0)
|
||||||
|
sys.stderr.write(stderr.read())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
exit_status = main(args=args, **kwargs)
|
exit_status = main(args=args, **kwargs)
|
||||||
if '--download' in args:
|
if '--download' in args:
|
||||||
# Let the progress reporter thread finish.
|
# Let the progress reporter thread finish.
|
||||||
time.sleep(.5)
|
time.sleep(.5)
|
||||||
|
except SystemExit:
|
||||||
|
if error_exit_ok:
|
||||||
|
exit_status = httpie.ExitStatus.ERROR
|
||||||
|
else:
|
||||||
|
dump_stderr()
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
|
stderr.seek(0)
|
||||||
sys.stderr.write(stderr.read())
|
sys.stderr.write(stderr.read())
|
||||||
raise
|
raise
|
||||||
except SystemExit:
|
else:
|
||||||
exit_status = httpie.ExitStatus.ERROR
|
if exit_status != httpie.ExitStatus.OK and not error_exit_ok:
|
||||||
|
dump_stderr()
|
||||||
|
raise Exception('Unexpected exit status: %s', exit_status)
|
||||||
|
|
||||||
stdout.seek(0)
|
stdout.seek(0)
|
||||||
stderr.seek(0)
|
stderr.seek(0)
|
||||||
output = stdout.read()
|
output = stdout.read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
output = output.decode('utf8')
|
output = output.decode('utf8')
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
@ -149,6 +169,9 @@ def http(*args, **kwargs):
|
|||||||
r.stderr = stderr.read()
|
r.stderr = stderr.read()
|
||||||
r.exit_status = exit_status
|
r.exit_status = exit_status
|
||||||
|
|
||||||
|
if r.exit_status != httpie.ExitStatus.OK:
|
||||||
|
sys.stderr.write(r.stderr)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""HTTP authentication-related tests."""
|
"""HTTP authentication-related tests."""
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -6,38 +8,45 @@ from tests import http, httpbin, HTTP_OK
|
|||||||
import httpie.input
|
import httpie.input
|
||||||
|
|
||||||
|
|
||||||
class TestAuth:
|
class AuthTest(TestCase):
|
||||||
def test_basic_auth(self):
|
def test_basic_auth(self):
|
||||||
r = http('--auth=user:password',
|
r = http('--auth=user:password', 'GET',
|
||||||
'GET', httpbin('/basic-auth/user/password'))
|
httpbin('/basic-auth/user/password'))
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert '"authenticated": true' in r
|
||||||
|
assert '"user": "user"' in r
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
requests.__version__ == '0.13.6',
|
requests.__version__ == '0.13.6',
|
||||||
reason='Redirects with prefetch=False are broken in Requests 0.13.6')
|
reason='Redirects with prefetch=False are broken in Requests 0.13.6')
|
||||||
def test_digest_auth(self):
|
def test_digest_auth(self):
|
||||||
r = http('--auth-type=digest', '--auth=user:password',
|
r = http('--auth-type=digest', '--auth=user:password', 'GET',
|
||||||
'GET', httpbin('/digest-auth/auth/user/password'))
|
httpbin('/digest-auth/auth/user/password'))
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert r'"authenticated": true' in r
|
||||||
|
assert r'"user": "user"', r
|
||||||
|
|
||||||
def test_password_prompt(self):
|
def test_password_prompt(self):
|
||||||
httpie.input.AuthCredentials._getpass = lambda self, prompt: 'password'
|
httpie.input.AuthCredentials._getpass = lambda self, prompt: 'password'
|
||||||
r = http('--auth', 'user', 'GET', httpbin('/basic-auth/user/password'))
|
r = http('--auth', 'user', 'GET', httpbin('/basic-auth/user/password'))
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert '"authenticated": true' in r
|
||||||
|
assert '"user": "user"' in r
|
||||||
|
|
||||||
def test_credentials_in_url(self):
|
def test_credentials_in_url(self):
|
||||||
r = http('GET', httpbin('/basic-auth/user/password',
|
url = httpbin('/basic-auth/user/password')
|
||||||
auth='user:password'))
|
url = 'http://user:password@' + url.split('http://', 1)[1]
|
||||||
|
r = http('GET', url)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert '"authenticated": true' in r
|
||||||
|
assert '"user": "user"' in r
|
||||||
|
|
||||||
def test_credentials_in_url_auth_flag_has_priority(self):
|
def test_credentials_in_url_auth_flag_has_priority(self):
|
||||||
"""When credentials are passed in URL and via -a at the same time,
|
"""When credentials are passed in URL and via -a at the same time,
|
||||||
then the ones from -a are used."""
|
then the ones from -a are used."""
|
||||||
r = http('--auth=user:password', 'GET',
|
url = httpbin('/basic-auth/user/password')
|
||||||
httpbin('/basic-auth/user/password', auth='user:wrong'))
|
url = 'http://user:wrong_password@' + url.split('http://', 1)[1]
|
||||||
|
r = http('--auth=user:password', 'GET', url)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert '"authenticated": true' in r
|
||||||
|
assert '"user": "user"' in r
|
||||||
|
@ -263,7 +263,8 @@ class TestNoOptions:
|
|||||||
assert 'GET /get HTTP/1.1' not in r
|
assert 'GET /get HTTP/1.1' not in r
|
||||||
|
|
||||||
def test_invalid_no_options(self):
|
def test_invalid_no_options(self):
|
||||||
r = http('--no-war', 'GET', httpbin('/get'))
|
r = http('--no-war', 'GET', httpbin('/get'),
|
||||||
|
error_exit_ok=True)
|
||||||
assert r.exit_status == 1
|
assert r.exit_status == 1
|
||||||
assert 'unrecognized arguments: --no-war' in r.stderr
|
assert 'unrecognized arguments: --no-war' in r.stderr
|
||||||
assert 'GET /get HTTP/1.1' not in r
|
assert 'GET /get HTTP/1.1' not in r
|
||||||
@ -279,6 +280,7 @@ class TestIgnoreStdin:
|
|||||||
assert FILE_CONTENT not in r, "Don't send stdin data."
|
assert FILE_CONTENT not in r, "Don't send stdin data."
|
||||||
|
|
||||||
def test_ignore_stdin_cannot_prompt_password(self):
|
def test_ignore_stdin_cannot_prompt_password(self):
|
||||||
r = http('--ignore-stdin', '--auth=no-password', httpbin('/get'))
|
r = http('--ignore-stdin', '--auth=no-password', httpbin('/get'),
|
||||||
|
error_exit_ok=True)
|
||||||
assert r.exit_status == ExitStatus.ERROR
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
assert 'because --ignore-stdin' in r.stderr
|
assert 'because --ignore-stdin' in r.stderr
|
||||||
|
@ -22,14 +22,15 @@ class TestExitStatus:
|
|||||||
reason='timeout broken in requests'
|
reason='timeout broken in requests'
|
||||||
' (https://github.com/jkbr/httpie/issues/185)')
|
' (https://github.com/jkbr/httpie/issues/185)')
|
||||||
def test_timeout_exit_status(self):
|
def test_timeout_exit_status(self):
|
||||||
r = http('--timeout=0.5', 'GET', httpbin('/delay/1'))
|
r = http('--timeout=0.5', 'GET', httpbin('/delay/1'),
|
||||||
|
error_exit_ok=True)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.exit_status == ExitStatus.ERROR_TIMEOUT
|
assert r.exit_status == ExitStatus.ERROR_TIMEOUT
|
||||||
|
|
||||||
def test_3xx_check_status_exits_3_and_stderr_when_stdout_redirected(self):
|
def test_3xx_check_status_exits_3_and_stderr_when_stdout_redirected(self):
|
||||||
env = TestEnvironment(stdout_isatty=False)
|
env = TestEnvironment(stdout_isatty=False)
|
||||||
r = http('--check-status', '--headers', 'GET', httpbin('/status/301'),
|
r = http('--check-status', '--headers', 'GET', httpbin('/status/301'),
|
||||||
env=env)
|
env=env, error_exit_ok=True)
|
||||||
assert 'HTTP/1.1 301' in r
|
assert 'HTTP/1.1 301' in r
|
||||||
assert r.exit_status == ExitStatus.ERROR_HTTP_3XX
|
assert r.exit_status == ExitStatus.ERROR_HTTP_3XX
|
||||||
assert '301 moved permanently' in r.stderr.lower()
|
assert '301 moved permanently' in r.stderr.lower()
|
||||||
@ -38,19 +39,22 @@ class TestExitStatus:
|
|||||||
requests.__version__ == '0.13.6',
|
requests.__version__ == '0.13.6',
|
||||||
reason='Redirects with prefetch=False are broken in Requests 0.13.6')
|
reason='Redirects with prefetch=False are broken in Requests 0.13.6')
|
||||||
def test_3xx_check_status_redirects_allowed_exits_0(self):
|
def test_3xx_check_status_redirects_allowed_exits_0(self):
|
||||||
r = http('--check-status', '--follow', 'GET', httpbin('/status/301'))
|
r = http('--check-status', '--follow', 'GET', httpbin('/status/301'),
|
||||||
|
error_exit_ok=True)
|
||||||
# The redirect will be followed so 200 is expected.
|
# The redirect will be followed so 200 is expected.
|
||||||
assert 'HTTP/1.1 200 OK' in r
|
assert 'HTTP/1.1 200 OK' in r
|
||||||
assert r.exit_status == ExitStatus.OK
|
assert r.exit_status == ExitStatus.OK
|
||||||
|
|
||||||
def test_4xx_check_status_exits_4(self):
|
def test_4xx_check_status_exits_4(self):
|
||||||
r = http('--check-status', 'GET', httpbin('/status/401'))
|
r = http('--check-status', 'GET', httpbin('/status/401'),
|
||||||
|
error_exit_ok=True)
|
||||||
assert 'HTTP/1.1 401' in r
|
assert 'HTTP/1.1 401' in r
|
||||||
assert r.exit_status == ExitStatus.ERROR_HTTP_4XX
|
assert r.exit_status == ExitStatus.ERROR_HTTP_4XX
|
||||||
# Also stderr should be empty since stdout isn't redirected.
|
# Also stderr should be empty since stdout isn't redirected.
|
||||||
assert not r.stderr
|
assert not r.stderr
|
||||||
|
|
||||||
def test_5xx_check_status_exits_5(self):
|
def test_5xx_check_status_exits_5(self):
|
||||||
r = http('--check-status', 'GET', httpbin('/status/500'))
|
r = http('--check-status', 'GET', httpbin('/status/500'),
|
||||||
|
error_exit_ok=True)
|
||||||
assert 'HTTP/1.1 500' in r
|
assert 'HTTP/1.1 500' in r
|
||||||
assert r.exit_status == ExitStatus.ERROR_HTTP_5XX
|
assert r.exit_status == ExitStatus.ERROR_HTTP_5XX
|
||||||
|
@ -13,12 +13,12 @@ class TestHTTPie:
|
|||||||
assert 'HTTPie data:' in r.stderr
|
assert 'HTTPie data:' in r.stderr
|
||||||
|
|
||||||
def test_help(self):
|
def test_help(self):
|
||||||
r = http('--help')
|
r = http('--help', error_exit_ok=True)
|
||||||
assert r.exit_status == httpie.ExitStatus.ERROR
|
assert r.exit_status == httpie.ExitStatus.ERROR
|
||||||
assert 'https://github.com/jkbr/httpie/issues' in r
|
assert 'https://github.com/jkbr/httpie/issues' in r
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
r = http('--version')
|
r = http('--version', error_exit_ok=True)
|
||||||
assert r.exit_status == httpie.ExitStatus.ERROR
|
assert r.exit_status == httpie.ExitStatus.ERROR
|
||||||
# FIXME: py3 has version in stdout, py2 in stderr
|
# FIXME: py3 has version in stdout, py2 in stderr
|
||||||
assert httpie.__version__ == r.stderr.strip() + r.strip()
|
assert httpie.__version__ == r.stderr.strip() + r.strip()
|
||||||
|
45
tests/test_unicode.py
Normal file
45
tests/test_unicode.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""
|
||||||
|
Various unicode handling related tests.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from tests import http, httpbin
|
||||||
|
|
||||||
|
|
||||||
|
JP_SUN = u'太陽'
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnicode:
|
||||||
|
|
||||||
|
def test_unicode_headers(self):
|
||||||
|
r = http('GET', httpbin('/headers'), u'Test:%s' % JP_SUN)
|
||||||
|
assert r.json['headers']['Test'] == JP_SUN
|
||||||
|
|
||||||
|
def test_unicode_form_item(self):
|
||||||
|
r = http('--form', 'POST', httpbin('/post'), u'test=%s' % JP_SUN)
|
||||||
|
assert r.json['form']['test'] == JP_SUN
|
||||||
|
|
||||||
|
def test_unicode_json_item(self):
|
||||||
|
r = http('--json', 'POST', httpbin('/post'), u'test=%s' % JP_SUN)
|
||||||
|
assert r.json['json']['test'] == JP_SUN
|
||||||
|
|
||||||
|
def test_unicode_raw_json_item(self):
|
||||||
|
r = http('--json', 'POST', httpbin('/post'), u'test:=["%s"]' % JP_SUN)
|
||||||
|
assert r.json['json']['test'] == [JP_SUN]
|
||||||
|
|
||||||
|
def test_unicode_url(self):
|
||||||
|
r = http(httpbin(u'/get?test=' + JP_SUN))
|
||||||
|
assert r.json['args']['test'] == JP_SUN
|
||||||
|
|
||||||
|
def test_unicode_basic_auth(self):
|
||||||
|
# it doesn't really authenticate us because httpbin
|
||||||
|
# doesn't interpret the utf8-encoded auth
|
||||||
|
http('--verbose', '--auth', u'test:%s' % JP_SUN,
|
||||||
|
httpbin(u'/basic-auth/test/' + JP_SUN))
|
||||||
|
|
||||||
|
def test_unicode_digest_auth(self):
|
||||||
|
# it doesn't really authenticate us because httpbin
|
||||||
|
# doesn't interpret the utf8-encoded auth
|
||||||
|
http('--auth-type=digest',
|
||||||
|
'--auth', u'test:%s' % JP_SUN,
|
||||||
|
httpbin(u'/digest-auth/auth/test/' + JP_SUN))
|
@ -45,11 +45,11 @@ class TestRequestBodyFromFilePath:
|
|||||||
def test_request_body_from_file_by_path_no_field_name_allowed(self):
|
def test_request_body_from_file_by_path_no_field_name_allowed(self):
|
||||||
env = TestEnvironment(stdin_isatty=True)
|
env = TestEnvironment(stdin_isatty=True)
|
||||||
r = http('POST', httpbin('/post'), 'field-name@' + FILE_PATH_ARG,
|
r = http('POST', httpbin('/post'), 'field-name@' + FILE_PATH_ARG,
|
||||||
env=env)
|
env=env, error_exit_ok=True)
|
||||||
assert 'perhaps you meant --form?' in r.stderr
|
assert 'perhaps you meant --form?' in r.stderr
|
||||||
|
|
||||||
def test_request_body_from_file_by_path_no_data_items_allowed(self):
|
def test_request_body_from_file_by_path_no_data_items_allowed(self):
|
||||||
env = TestEnvironment(stdin_isatty=False)
|
env = TestEnvironment(stdin_isatty=False)
|
||||||
r = http('POST', httpbin('/post'), '@' + FILE_PATH_ARG, 'foo=bar',
|
r = http('POST', httpbin('/post'), '@' + FILE_PATH_ARG, 'foo=bar',
|
||||||
env=env)
|
env=env, error_exit_ok=True)
|
||||||
assert 'cannot be mixed' in r.stderr
|
assert 'cannot be mixed' in r.stderr
|
||||||
|
@ -23,5 +23,6 @@ class TestFakeWindows:
|
|||||||
output_file = os.path.join(
|
output_file = os.path.join(
|
||||||
tempfile.gettempdir(), '__httpie_test_output__')
|
tempfile.gettempdir(), '__httpie_test_output__')
|
||||||
r = http('--output', output_file,
|
r = http('--output', output_file,
|
||||||
'--pretty=all', 'GET', httpbin('/get'), env=env)
|
'--pretty=all', 'GET', httpbin('/get'),
|
||||||
|
env=env, error_exit_ok=True)
|
||||||
assert 'Only terminal output can be colorized on Windows' in r.stderr
|
assert 'Only terminal output can be colorized on Windows' in r.stderr
|
||||||
|
Loading…
Reference in New Issue
Block a user