mirror of
https://github.com/httpie/cli.git
synced 2025-01-11 16:18:44 +01:00
Switched to "==" a the separator for URL params.
Also refactored item escaping.
This commit is contained in:
parent
728a1a195b
commit
9944def703
19
README.rst
19
README.rst
@ -83,8 +83,8 @@ File fields (``field@/path/to/file``)
|
|||||||
``screenshot@/path/to/file.png``. The presence of a file field results into
|
``screenshot@/path/to/file.png``. The presence of a file field results into
|
||||||
a ``multipart/form-data`` request.
|
a ``multipart/form-data`` request.
|
||||||
|
|
||||||
Query string parameters (``name=:value``)
|
Query string parameters (``name==value``)
|
||||||
Appends the given name/value pair as a query string parameter to the URL.
|
Appends the given name/value pair as a query string parameter to the URL.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@ -127,11 +127,12 @@ The above will send the same request as if the following HTML form were submitte
|
|||||||
<input type="file" name="cv" />
|
<input type="file" name="cv" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
Query string parameters can be added to any request::
|
**Query string parameters** can be added to any request without having to quote
|
||||||
|
the ``&`` characters::
|
||||||
|
|
||||||
http GET example.com/ search=:donuts
|
http GET example.com/ search==donuts in==fridge
|
||||||
|
|
||||||
Will GET the URL "example.com/?search=donuts".
|
Will ``GET` the URL ``http://example.com/?search=donuts&in=fridge``.
|
||||||
|
|
||||||
A whole request body can be passed in via **``stdin``** instead, in which
|
A whole request body can be passed in via **``stdin``** instead, in which
|
||||||
case it will be used with no further processing::
|
case it will be used with no further processing::
|
||||||
@ -151,7 +152,7 @@ the second one does via ``stdin``::
|
|||||||
|
|
||||||
http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
|
http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
|
||||||
|
|
||||||
Note that when the output is redirected (like the examples above), HTTPie
|
Note that when the **output is redirected** (like the examples above), HTTPie
|
||||||
applies a different set of defaults then for console output. Namely colors
|
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
|
aren't used (can be forced with ``--pretty``) and only the response body
|
||||||
gets printed (can be overwritten with ``--print``).
|
gets printed (can be overwritten with ``--print``).
|
||||||
@ -165,7 +166,7 @@ request will send the verbatim contents of the file with
|
|||||||
|
|
||||||
http PUT httpbin.org/put @/data/file.xml
|
http PUT httpbin.org/put @/data/file.xml
|
||||||
|
|
||||||
When using HTTPie from shell scripts you might want to use the
|
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
|
``--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
|
HTTP status is one of ``3xx``, ``4xx``, or ``5xx``. The exit status will
|
||||||
be ``3`` (unless ``--allow-redirects`` is set), ``4``, or ``5``
|
be ``3`` (unless ``--allow-redirects`` is set), ``4``, or ``5``
|
||||||
@ -213,7 +214,7 @@ See ``http -h`` for more details::
|
|||||||
separator used. It can be an HTTP header
|
separator used. It can be an HTTP header
|
||||||
(header:value), a data field to be used in the request
|
(header:value), a data field to be used in the request
|
||||||
body (field_name=value), a raw JSON data field
|
body (field_name=value), a raw JSON data field
|
||||||
(field_name:=value), a query parameter (name=:value),
|
(field_name:=value), a query parameter (name=value),
|
||||||
or a file field (field_name@/path/to/file). You can
|
or a file field (field_name@/path/to/file). You can
|
||||||
use a backslash to escape a colliding separator in the
|
use a backslash to escape a colliding separator in the
|
||||||
field name.
|
field name.
|
||||||
@ -333,7 +334,7 @@ Changelog
|
|||||||
the new default behaviour is to only print the response body.
|
the new default behaviour is to only print the response body.
|
||||||
(It can still be overriden via the ``--print`` flag.)
|
(It can still be overriden via the ``--print`` flag.)
|
||||||
* Improved highlighing of HTTP headers.
|
* Improved highlighing of HTTP headers.
|
||||||
* Added query string parameters (param=:value).
|
* Added query string parameters (param==value).
|
||||||
* Added support for terminal colors under Windows.
|
* Added support for terminal colors under Windows.
|
||||||
* `0.2.5 <https://github.com/jkbr/httpie/compare/0.2.2...0.2.5>`_ (2012-07-17)
|
* `0.2.5 <https://github.com/jkbr/httpie/compare/0.2.2...0.2.5>`_ (2012-07-17)
|
||||||
* Unicode characters in prettified JSON now don't get escaped for
|
* Unicode characters in prettified JSON now don't get escaped for
|
||||||
|
@ -146,7 +146,7 @@ parser.add_argument(
|
|||||||
|
|
||||||
# ``requests.request`` keyword arguments.
|
# ``requests.request`` keyword arguments.
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON),
|
'--auth', '-a', type=cliparse.AuthCredentialsArgType(cliparse.SEP_COMMON),
|
||||||
help=_('''
|
help=_('''
|
||||||
username:password.
|
username:password.
|
||||||
If only the username is provided (-a username),
|
If only the username is provided (-a username),
|
||||||
@ -174,7 +174,7 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--proxy', default=[], action='append',
|
'--proxy', default=[], action='append',
|
||||||
type=cliparse.KeyValueType(cliparse.SEP_COMMON),
|
type=cliparse.KeyValueArgType(cliparse.SEP_COMMON),
|
||||||
help=_('''
|
help=_('''
|
||||||
String mapping protocol to the URL of the proxy
|
String mapping protocol to the URL of the proxy
|
||||||
(e.g. http:foo.bar:3128).
|
(e.g. http:foo.bar:3128).
|
||||||
@ -221,7 +221,7 @@ parser.add_argument(
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'items', nargs='*',
|
'items', nargs='*',
|
||||||
metavar='ITEM',
|
metavar='ITEM',
|
||||||
type=cliparse.KeyValueType(
|
type=cliparse.KeyValueArgType(
|
||||||
cliparse.SEP_COMMON,
|
cliparse.SEP_COMMON,
|
||||||
cliparse.SEP_QUERY,
|
cliparse.SEP_QUERY,
|
||||||
cliparse.SEP_DATA,
|
cliparse.SEP_DATA,
|
||||||
@ -233,7 +233,7 @@ parser.add_argument(
|
|||||||
separator used. It can be an HTTP header (header:value),
|
separator used. It can be an HTTP header (header:value),
|
||||||
a data field to be used in the request body (field_name=value),
|
a data field to be used in the request body (field_name=value),
|
||||||
a raw JSON data field (field_name:=value),
|
a raw JSON data field (field_name:=value),
|
||||||
a query parameter (name=:value),
|
a query parameter (name==value),
|
||||||
or a file field (field_name@/path/to/file).
|
or a file field (field_name@/path/to/file).
|
||||||
You can use a backslash to escape a colliding
|
You can use a backslash to escape a colliding
|
||||||
separator in the field name.
|
separator in the field name.
|
||||||
|
@ -16,6 +16,7 @@ except ImportError:
|
|||||||
OrderedDict = dict
|
OrderedDict = dict
|
||||||
|
|
||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
from requests.compat import str
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ SEP_HEADERS = SEP_COMMON
|
|||||||
SEP_DATA = '='
|
SEP_DATA = '='
|
||||||
SEP_DATA_RAW_JSON = ':='
|
SEP_DATA_RAW_JSON = ':='
|
||||||
SEP_FILES = '@'
|
SEP_FILES = '@'
|
||||||
SEP_QUERY = '=:'
|
SEP_QUERY = '=='
|
||||||
DATA_ITEM_SEPARATORS = [
|
DATA_ITEM_SEPARATORS = [
|
||||||
SEP_DATA,
|
SEP_DATA,
|
||||||
SEP_DATA_RAW_JSON,
|
SEP_DATA_RAW_JSON,
|
||||||
@ -61,7 +62,6 @@ class Parser(argparse.ArgumentParser):
|
|||||||
|
|
||||||
if not env.stdin_isatty:
|
if not env.stdin_isatty:
|
||||||
self._body_from_file(args, env.stdin)
|
self._body_from_file(args, env.stdin)
|
||||||
|
|
||||||
if args.auth and not args.auth.has_password():
|
if args.auth and not args.auth.has_password():
|
||||||
# stdin has already been read (if not a tty) so
|
# stdin has already been read (if not a tty) so
|
||||||
# it's save to prompt now.
|
# it's save to prompt now.
|
||||||
@ -99,7 +99,7 @@ class Parser(argparse.ArgumentParser):
|
|||||||
# - Set `args.url` correctly.
|
# - Set `args.url` correctly.
|
||||||
# - Parse the first item and move it to `args.items[0]`.
|
# - Parse the first item and move it to `args.items[0]`.
|
||||||
|
|
||||||
item = KeyValueType(
|
item = KeyValueArgType(
|
||||||
SEP_COMMON,
|
SEP_COMMON,
|
||||||
SEP_QUERY,
|
SEP_QUERY,
|
||||||
SEP_DATA,
|
SEP_DATA,
|
||||||
@ -119,20 +119,20 @@ class Parser(argparse.ArgumentParser):
|
|||||||
def _parse_items(self, args):
|
def _parse_items(self, args):
|
||||||
"""
|
"""
|
||||||
Parse `args.items` into `args.headers`,
|
Parse `args.items` into `args.headers`,
|
||||||
`args.data`, `args.queries`, and `args.files`.
|
`args.data`, `args.`, and `args.files`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
args.headers = CaseInsensitiveDict()
|
args.headers = CaseInsensitiveDict()
|
||||||
args.headers['User-Agent'] = DEFAULT_UA
|
args.headers['User-Agent'] = DEFAULT_UA
|
||||||
args.data = OrderedDict()
|
args.data = OrderedDict()
|
||||||
args.files = OrderedDict()
|
args.files = OrderedDict()
|
||||||
args.queries = CaseInsensitiveDict()
|
args.params = OrderedDict()
|
||||||
try:
|
try:
|
||||||
parse_items(items=args.items,
|
parse_items(items=args.items,
|
||||||
headers=args.headers,
|
headers=args.headers,
|
||||||
data=args.data,
|
data=args.data,
|
||||||
files=args.files,
|
files=args.files,
|
||||||
queries=args.queries)
|
params=args.params)
|
||||||
except ParseError as e:
|
except ParseError as e:
|
||||||
if args.traceback:
|
if args.traceback:
|
||||||
raise
|
raise
|
||||||
@ -195,49 +195,91 @@ class KeyValue(object):
|
|||||||
return self.__dict__ == other.__dict__
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
|
||||||
class KeyValueType(object):
|
class KeyValueArgType(object):
|
||||||
"""A type used with `argparse`."""
|
"""
|
||||||
|
A key-value pair argument type used with `argparse`.
|
||||||
|
|
||||||
|
Parses a key-value arg and constructs a `KeyValue` instance.
|
||||||
|
Used for headers, form data, and other key-value pair types.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
key_value_class = KeyValue
|
key_value_class = KeyValue
|
||||||
|
|
||||||
def __init__(self, *separators):
|
def __init__(self, *separators):
|
||||||
self.separators = separators
|
self.separators = separators
|
||||||
self.escapes = ['\\\\' + sep for sep in separators]
|
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
found = {}
|
"""
|
||||||
found_escapes = []
|
Parse `string` and return `self.key_value_class()` instance.
|
||||||
for esc in self.escapes:
|
|
||||||
found_escapes += [m.span() for m in re.finditer(esc, string)]
|
|
||||||
for sep in self.separators:
|
|
||||||
matches = re.finditer(sep, string)
|
|
||||||
for match in matches:
|
|
||||||
start, end = match.span()
|
|
||||||
inside_escape = False
|
|
||||||
for estart, eend in found_escapes:
|
|
||||||
if start >= estart and end <= eend:
|
|
||||||
inside_escape = True
|
|
||||||
break
|
|
||||||
if start in found and len(found[start]) > len(sep):
|
|
||||||
break
|
|
||||||
if not inside_escape:
|
|
||||||
found[start] = sep
|
|
||||||
|
|
||||||
if not found:
|
The best of `self.separators` is determined (first found, longest).
|
||||||
|
Back slash escaped characters aren't considered as separators
|
||||||
|
(or parts thereof). Literal back slash characters have to be escaped
|
||||||
|
as well (r'\\').
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Escaped(str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tokenize(s):
|
||||||
|
"""
|
||||||
|
r'foo\=bar\\baz'
|
||||||
|
=> ['foo', Escaped('='), 'bar', Escaped('\'), 'baz']
|
||||||
|
|
||||||
|
"""
|
||||||
|
tokens = ['']
|
||||||
|
esc = False
|
||||||
|
for c in s:
|
||||||
|
if esc:
|
||||||
|
tokens.extend([Escaped(c), ''])
|
||||||
|
esc = False
|
||||||
|
else:
|
||||||
|
if c == '\\':
|
||||||
|
esc = True
|
||||||
|
else:
|
||||||
|
tokens[-1] += c
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
tokens = tokenize(string)
|
||||||
|
|
||||||
|
# Sorting by length ensures that the longest one will be
|
||||||
|
# chosen as it will overwrite any shorter ones starting
|
||||||
|
# at the same position in the `found` dictionary.
|
||||||
|
separators = sorted(self.separators, key=len)
|
||||||
|
|
||||||
|
for i, token in enumerate(tokens):
|
||||||
|
|
||||||
|
if isinstance(token, Escaped):
|
||||||
|
continue
|
||||||
|
|
||||||
|
found = {}
|
||||||
|
for sep in separators:
|
||||||
|
pos = token.find(sep)
|
||||||
|
if pos != -1:
|
||||||
|
found[pos] = sep
|
||||||
|
|
||||||
|
if found:
|
||||||
|
# Starting first, longest separator found.
|
||||||
|
sep = found[min(found.keys())]
|
||||||
|
|
||||||
|
key, value = token.split(sep, 1)
|
||||||
|
|
||||||
|
# Any preceding tokens are part of the key.
|
||||||
|
key = ''.join(tokens[:i]) + key
|
||||||
|
|
||||||
|
# Any following tokens are part of the value.
|
||||||
|
value += ''.join(tokens[i + 1:])
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
raise argparse.ArgumentTypeError(
|
raise argparse.ArgumentTypeError(
|
||||||
'"%s" is not a valid value' % string)
|
'"%s" is not a valid value' % string)
|
||||||
|
|
||||||
# split the string at the earliest non-escaped separator.
|
return self.key_value_class(
|
||||||
seploc = min(found.keys())
|
key=key, value=value, sep=sep, orig=string)
|
||||||
sep = found[seploc]
|
|
||||||
key = string[:seploc]
|
|
||||||
value = string[seploc + len(sep):]
|
|
||||||
|
|
||||||
# remove escape chars
|
|
||||||
for sepstr in self.separators:
|
|
||||||
key = key.replace('\\' + sepstr, sepstr)
|
|
||||||
value = value.replace('\\' + sepstr, sepstr)
|
|
||||||
return self.key_value_class(key=key, value=value, sep=sep, orig=string)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthCredentials(KeyValue):
|
class AuthCredentials(KeyValue):
|
||||||
@ -260,13 +302,13 @@ class AuthCredentials(KeyValue):
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
class AuthCredentialsType(KeyValueType):
|
class AuthCredentialsArgType(KeyValueArgType):
|
||||||
|
|
||||||
key_value_class = AuthCredentials
|
key_value_class = AuthCredentials
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
try:
|
try:
|
||||||
return super(AuthCredentialsType, self).__call__(string)
|
return super(AuthCredentialsArgType, self).__call__(string)
|
||||||
except argparse.ArgumentTypeError:
|
except argparse.ArgumentTypeError:
|
||||||
# No password provided, will prompt for it later.
|
# No password provided, will prompt for it later.
|
||||||
return self.key_value_class(
|
return self.key_value_class(
|
||||||
@ -277,10 +319,10 @@ class AuthCredentialsType(KeyValueType):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_items(items, data=None, headers=None, files=None, queries=None):
|
def parse_items(items, data=None, headers=None, files=None, params=None):
|
||||||
"""
|
"""
|
||||||
Parse `KeyValueType` `items` into `data`, `headers`, `files`,
|
Parse `KeyValue` `items` into `data`, `headers`, `files`,
|
||||||
and `queries`.
|
and `params`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if headers is None:
|
if headers is None:
|
||||||
@ -289,15 +331,15 @@ def parse_items(items, data=None, headers=None, files=None, queries=None):
|
|||||||
data = {}
|
data = {}
|
||||||
if files is None:
|
if files is None:
|
||||||
files = {}
|
files = {}
|
||||||
if queries is None:
|
if params is None:
|
||||||
queries = {}
|
params = {}
|
||||||
for item in items:
|
for item in items:
|
||||||
value = item.value
|
value = item.value
|
||||||
key = item.key
|
key = item.key
|
||||||
if item.sep == SEP_HEADERS:
|
if item.sep == SEP_HEADERS:
|
||||||
target = headers
|
target = headers
|
||||||
elif item.sep == SEP_QUERY:
|
elif item.sep == SEP_QUERY:
|
||||||
target = queries
|
target = params
|
||||||
elif item.sep == SEP_FILES:
|
elif item.sep == SEP_FILES:
|
||||||
try:
|
try:
|
||||||
value = open(os.path.expanduser(item.value), 'r')
|
value = open(os.path.expanduser(item.value), 'r')
|
||||||
@ -322,4 +364,4 @@ def parse_items(items, data=None, headers=None, files=None, queries=None):
|
|||||||
|
|
||||||
target[key] = value
|
target[key] = value
|
||||||
|
|
||||||
return headers, data, files, queries
|
return headers, data, files, params
|
||||||
|
@ -53,7 +53,7 @@ def get_response(args):
|
|||||||
proxies=dict((p.key, p.value) for p in args.proxy),
|
proxies=dict((p.key, p.value) for p in args.proxy),
|
||||||
files=args.files,
|
files=args.files,
|
||||||
allow_redirects=args.allow_redirects,
|
allow_redirects=args.allow_redirects,
|
||||||
params=args.queries,
|
params=args.params,
|
||||||
)
|
)
|
||||||
|
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
# coding=utf8
|
# coding=utf8
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -598,7 +599,7 @@ class ExitStatusTest(BaseTestCase):
|
|||||||
class ItemParsingTest(BaseTestCase):
|
class ItemParsingTest(BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.key_value_type = cliparse.KeyValueType(
|
self.key_value_type = cliparse.KeyValueArgType(
|
||||||
cliparse.SEP_HEADERS,
|
cliparse.SEP_HEADERS,
|
||||||
cliparse.SEP_QUERY,
|
cliparse.SEP_QUERY,
|
||||||
cliparse.SEP_DATA,
|
cliparse.SEP_DATA,
|
||||||
@ -613,7 +614,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
lambda: self.key_value_type(item))
|
lambda: self.key_value_type(item))
|
||||||
|
|
||||||
def test_escape(self):
|
def test_escape(self):
|
||||||
headers, data, files, queries = cliparse.parse_items([
|
headers, data, files, params = cliparse.parse_items([
|
||||||
# headers
|
# headers
|
||||||
self.key_value_type('foo\\:bar:baz'),
|
self.key_value_type('foo\\:bar:baz'),
|
||||||
self.key_value_type('jack\\@jill:hill'),
|
self.key_value_type('jack\\@jill:hill'),
|
||||||
@ -632,15 +633,15 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
self.assertIn('bar@baz', files)
|
self.assertIn('bar@baz', files)
|
||||||
|
|
||||||
def test_escape_longsep(self):
|
def test_escape_longsep(self):
|
||||||
headers, data, files, queries = cliparse.parse_items([
|
headers, data, files, params = cliparse.parse_items([
|
||||||
self.key_value_type('bob\\:==foo'),
|
self.key_value_type('bob\\:==foo'),
|
||||||
])
|
])
|
||||||
self.assertDictEqual(data, {
|
self.assertDictEqual(params, {
|
||||||
'bob:=': 'foo',
|
'bob:': 'foo',
|
||||||
})
|
})
|
||||||
|
|
||||||
def test_valid_items(self):
|
def test_valid_items(self):
|
||||||
headers, data, files, queries = cliparse.parse_items([
|
headers, data, files, params = cliparse.parse_items([
|
||||||
self.key_value_type('string=value'),
|
self.key_value_type('string=value'),
|
||||||
self.key_value_type('header:value'),
|
self.key_value_type('header:value'),
|
||||||
self.key_value_type('list:=["a", 1, {}, false]'),
|
self.key_value_type('list:=["a", 1, {}, false]'),
|
||||||
@ -649,7 +650,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
self.key_value_type('ed='),
|
self.key_value_type('ed='),
|
||||||
self.key_value_type('bool:=true'),
|
self.key_value_type('bool:=true'),
|
||||||
self.key_value_type('test-file@%s' % TEST_FILE_PATH),
|
self.key_value_type('test-file@%s' % TEST_FILE_PATH),
|
||||||
self.key_value_type('query=:value'),
|
self.key_value_type('query==value'),
|
||||||
])
|
])
|
||||||
self.assertDictEqual(headers, {
|
self.assertDictEqual(headers, {
|
||||||
'header': 'value',
|
'header': 'value',
|
||||||
@ -660,21 +661,13 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
"string": "value",
|
"string": "value",
|
||||||
"bool": True,
|
"bool": True,
|
||||||
"list": ["a", 1, {}, False],
|
"list": ["a", 1, {}, False],
|
||||||
"obj": {"a": "b"}
|
"obj": {"a": "b"},
|
||||||
})
|
})
|
||||||
self.assertDictEqual(queries, {
|
self.assertDictEqual(params, {
|
||||||
'query': 'value',
|
'query': 'value',
|
||||||
})
|
})
|
||||||
self.assertIn('test-file', files)
|
self.assertIn('test-file', files)
|
||||||
|
|
||||||
def test_query_string(self):
|
|
||||||
headers, data, files, queries = cliparse.parse_items([
|
|
||||||
self.key_value_type('query=:value'),
|
|
||||||
])
|
|
||||||
self.assertDictEqual(queries, {
|
|
||||||
'query': 'value',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class ArgumentParserTestCase(unittest.TestCase):
|
class ArgumentParserTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user