mirror of
https://github.com/httpie/cli.git
synced 2024-11-07 16:34:35 +01:00
parent
85b3a016eb
commit
681b652bf9
24
README.rst
24
README.rst
@ -118,6 +118,7 @@ Flags
|
||||
^^^^^
|
||||
Most of the flags mirror the arguments understood by ``requests.request``. See ``http -h`` for more details::
|
||||
|
||||
$ http --help
|
||||
usage: http [-h] [--version] [--json | --form] [--traceback]
|
||||
[--pretty | --ugly]
|
||||
[--print OUTPUT_OPTIONS | --verbose | --headers | --body]
|
||||
@ -147,13 +148,15 @@ Most of the flags mirror the arguments understood by ``requests.request``. See `
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--version show program's version number and exit
|
||||
--json, -j (default) Data items are serialized as a JSON object.
|
||||
The Content-Type and Accept headers are set to
|
||||
application/json (if not set via the command line).
|
||||
--form, -f Data items are serialized as form fields. The Content-
|
||||
Type is set to application/x-www-form-urlencoded (if
|
||||
not specifid). The presence of any file fields results
|
||||
into a multipart/form-data request.
|
||||
--json, -j (default) Data items from the command line are
|
||||
serialized as a JSON object. The Content-Type and
|
||||
Accept headers are set to application/json (if not
|
||||
specified).
|
||||
--form, -f Data items from the command line are serialized as
|
||||
form fields. The Content-Type is set to application/x
|
||||
-www-form-urlencoded (if not specified). The presence
|
||||
of any file fields results into a multipart/form-data
|
||||
request.
|
||||
--traceback Print exception traceback should one occur.
|
||||
--pretty If stdout is a terminal, the response is prettified by
|
||||
default (colorized and indented if it is JSON). This
|
||||
@ -180,8 +183,8 @@ Most of the flags mirror the arguments understood by ``requests.request``. See `
|
||||
make sure that the $TERM environment variable is set
|
||||
to "xterm-256color" or similar (e.g., via `export TERM
|
||||
=xterm-256color' in your ~/.bashrc).
|
||||
--auth AUTH, -a AUTH username:password. If the password is omitted, HTTPie
|
||||
will prompt for it.
|
||||
--auth AUTH, -a AUTH username:password. If the password is omitted (-a
|
||||
username), HTTPie will prompt for it.
|
||||
--auth-type {basic,digest}
|
||||
The authentication mechanism to be used. Defaults to
|
||||
"basic".
|
||||
@ -223,7 +226,10 @@ Changelog
|
||||
---------
|
||||
|
||||
* `0.2.3dev <https://github.com/jkbr/httpie/compare/0.2.2...master>`_
|
||||
* --auth now prompts for a password if none is provided.
|
||||
* Added support for request payloads from a file path with automatic ``Content-Type`` (``http URL @/path``).
|
||||
* Fixed missing query string when displaing the request headers via ``--verbose``.
|
||||
* Fixed Content-Type for requests with no data.
|
||||
* `0.2.2 <https://github.com/jkbr/httpie/compare/0.2.1...0.2.2>`_ (2012-06-24)
|
||||
* The ``METHOD`` positional argument can now be omitted (defaults to ``GET``, or to ``POST`` with data).
|
||||
* Fixed --verbose --form.
|
||||
|
@ -123,10 +123,10 @@ parser.add_argument(
|
||||
|
||||
# ``requests.request`` keyword arguments.
|
||||
parser.add_argument(
|
||||
'--auth', '-a', type=cliparse.AuthCredentials(cliparse.SEP_COMMON),
|
||||
'--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON),
|
||||
help=_('''
|
||||
username:password.
|
||||
If the password is omitted, HTTPie will prompt for it.
|
||||
If the password is omitted (-a username), HTTPie will prompt for it.
|
||||
'''),
|
||||
)
|
||||
|
||||
|
@ -8,9 +8,7 @@ import re
|
||||
import json
|
||||
import argparse
|
||||
import mimetypes
|
||||
|
||||
from collections import namedtuple
|
||||
from getpass import getpass
|
||||
import getpass
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
@ -54,13 +52,22 @@ class Parser(argparse.ArgumentParser):
|
||||
def parse_args(self, args=None, namespace=None,
|
||||
stdin=sys.stdin,
|
||||
stdin_isatty=sys.stdin.isatty()):
|
||||
|
||||
args = super(Parser, self).parse_args(args, namespace)
|
||||
|
||||
self._validate_output_options(args)
|
||||
self._validate_auth_options(args)
|
||||
self._guess_method(args, stdin_isatty)
|
||||
self._parse_items(args)
|
||||
|
||||
if not stdin_isatty:
|
||||
self._body_from_file(args, stdin)
|
||||
|
||||
if args.auth and not args.auth.has_password():
|
||||
# stdin has already been read (if not a tty) so
|
||||
# it's save to prompt now.
|
||||
args.auth.prompt_password()
|
||||
|
||||
return args
|
||||
|
||||
def _body_from_file(self, args, f):
|
||||
@ -161,12 +168,24 @@ class ParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
KeyValue = namedtuple('KeyValue', ['key', 'value', 'sep', 'orig'])
|
||||
class KeyValue(object):
|
||||
"""Base key-value pair parsed from CLI."""
|
||||
|
||||
def __init__(self, key, value, sep, orig):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.sep = sep
|
||||
self.orig = orig
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
|
||||
class KeyValueType(object):
|
||||
"""A type used with `argparse`."""
|
||||
|
||||
key_value_class = KeyValue
|
||||
|
||||
def __init__(self, *separators):
|
||||
self.separators = separators
|
||||
self.escapes = ['\\\\' + sep for sep in separators]
|
||||
@ -202,21 +221,43 @@ class KeyValueType(object):
|
||||
for sepstr in self.separators:
|
||||
key = key.replace('\\' + sepstr, sepstr)
|
||||
value = value.replace('\\' + sepstr, sepstr)
|
||||
return KeyValue(key=key, value=value, sep=sep, orig=string)
|
||||
return self.key_value_class(key=key, value=value, sep=sep, orig=string)
|
||||
|
||||
|
||||
class AuthCredentials(KeyValueType):
|
||||
class AuthCredentials(KeyValue):
|
||||
"""
|
||||
Represents parsed credentials.
|
||||
|
||||
"""
|
||||
def _getpass(self, prompt):
|
||||
return getpass.getpass(prompt)
|
||||
|
||||
def has_password(self):
|
||||
return self.value is not None
|
||||
|
||||
def prompt_password(self):
|
||||
try:
|
||||
self.value = self._getpass("Password for user '%s': " % self.key)
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class AuthCredentialsType(KeyValueType):
|
||||
|
||||
key_value_class = AuthCredentials
|
||||
|
||||
def __call__(self, string):
|
||||
try:
|
||||
return super(AuthCredentials, self).__call__(string)
|
||||
return super(AuthCredentialsType, self).__call__(string)
|
||||
except argparse.ArgumentTypeError:
|
||||
try:
|
||||
password = getpass("Password for user '%s': " % string)
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
sys.stderr.write('\n')
|
||||
sys.exit(0)
|
||||
return KeyValue(key=string, value=password, sep=SEP_COMMON,
|
||||
orig=string)
|
||||
# No password provided, will prompt for it later.
|
||||
return self.key_value_class(
|
||||
key=string,
|
||||
value=None,
|
||||
sep=SEP_COMMON,
|
||||
orig=string
|
||||
)
|
||||
|
||||
|
||||
def parse_items(items, data=None, headers=None, files=None):
|
||||
|
@ -286,6 +286,16 @@ class AuthTest(BaseTestCase):
|
||||
self.assertIn('"authenticated": true', r)
|
||||
self.assertIn('"user": "user"', r)
|
||||
|
||||
def test_password_prompt(self):
|
||||
cliparse.AuthCredentials._getpass = lambda self, prompt: 'password'
|
||||
|
||||
r = http('--auth', 'user',
|
||||
'GET', 'httpbin.org/basic-auth/user/password')
|
||||
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn('"authenticated": true', r)
|
||||
self.assertIn('"user": "user"', r)
|
||||
|
||||
|
||||
#################################################################
|
||||
# CLI argument parsing related tests.
|
||||
|
Loading…
Reference in New Issue
Block a user