From 06ea36aaa4136316bbd6b04df77eed44ff15c6b3 Mon Sep 17 00:00:00 2001 From: Jake Basile Date: Wed, 18 Jul 2012 20:44:09 -0500 Subject: [PATCH 1/3] Added the ability to pass query string parameters. --- httpie/__main__.py | 2 ++ httpie/cli.py | 1 + httpie/cliparse.py | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/httpie/__main__.py b/httpie/__main__.py index 4c9565c0..36a4266e 100644 --- a/httpie/__main__.py +++ b/httpie/__main__.py @@ -41,6 +41,7 @@ def _get_response(args): # the `Content-Type` for us. args.headers['Content-Type'] = TYPE_FORM + # Fire the request. try: credentials = None @@ -61,6 +62,7 @@ def _get_response(args): proxies=dict((p.key, p.value) for p in args.proxy), files=args.files, allow_redirects=args.allow_redirects, + params=args.queries, ) except (KeyboardInterrupt, SystemExit): diff --git a/httpie/cli.py b/httpie/cli.py index 1c23a422..3449bf63 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -203,6 +203,7 @@ parser.add_argument( metavar='ITEM', type=cliparse.KeyValueType( cliparse.SEP_COMMON, + cliparse.SEP_QUERY, cliparse.SEP_DATA, cliparse.SEP_DATA_RAW_JSON, cliparse.SEP_FILES diff --git a/httpie/cliparse.py b/httpie/cliparse.py index 9b8da667..6ef26d84 100644 --- a/httpie/cliparse.py +++ b/httpie/cliparse.py @@ -25,6 +25,7 @@ SEP_HEADERS = SEP_COMMON SEP_DATA = '=' SEP_DATA_RAW_JSON = ':=' SEP_FILES = '@' +SEP_QUERY = '=:' DATA_ITEM_SEPARATORS = [ SEP_DATA, SEP_DATA_RAW_JSON, @@ -102,6 +103,7 @@ class Parser(argparse.ArgumentParser): item = KeyValueType( SEP_COMMON, + SEP_QUERY, SEP_DATA, SEP_DATA_RAW_JSON, SEP_FILES).__call__(args.url) @@ -118,16 +120,17 @@ class Parser(argparse.ArgumentParser): def _parse_items(self, args): """ - Parse `args.items` into `args.headers`, `args.data` and `args.files`. + Parse `args.items` into `args.headers`, `args.data`, `args.queries`, and `args.files`. """ args.headers = CaseInsensitiveDict() args.headers['User-Agent'] = DEFAULT_UA args.data = OrderedDict() args.files = OrderedDict() + args.queries = CaseInsensitiveDict() try: parse_items(items=args.items, headers=args.headers, - data=args.data, files=args.files) + data=args.data, files=args.files, queries=args.queries) except ParseError as e: if args.traceback: raise @@ -207,6 +210,8 @@ class KeyValueType(object): 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 @@ -264,19 +269,23 @@ class AuthCredentialsType(KeyValueType): ) -def parse_items(items, data=None, headers=None, files=None): - """Parse `KeyValueType` `items` into `data`, `headers` and `files`.""" +def parse_items(items, data=None, headers=None, files=None, queries=None): + """Parse `KeyValueType` `items` into `data`, `headers`, `files`, and `queries`.""" if headers is None: headers = {} if data is None: data = {} if files is None: files = {} + if queries is None: + queries = {} for item in items: value = item.value key = item.key if item.sep == SEP_HEADERS: target = headers + elif item.sep == SEP_QUERY: + target = queries elif item.sep == SEP_FILES: try: value = open(os.path.expanduser(item.value), 'r') @@ -301,4 +310,4 @@ def parse_items(items, data=None, headers=None, files=None): target[key] = value - return headers, data, files + return headers, data, files, queries From 24d6331d15c0cf94507e848de9db22618b611aec Mon Sep 17 00:00:00 2001 From: Jake Basile Date: Wed, 18 Jul 2012 21:16:08 -0500 Subject: [PATCH 2/3] Added a bit of testing for the new query string parameters. --- tests/tests.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index c01e361d..ef3a5da0 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -341,6 +341,7 @@ class ItemParsingTest(BaseTestCase): def setUp(self): self.key_value_type = cliparse.KeyValueType( cliparse.SEP_HEADERS, + cliparse.SEP_QUERY, cliparse.SEP_DATA, cliparse.SEP_DATA_RAW_JSON, cliparse.SEP_FILES, @@ -353,7 +354,7 @@ class ItemParsingTest(BaseTestCase): lambda: self.key_value_type(item)) def test_escape(self): - headers, data, files = cliparse.parse_items([ + headers, data, files, queries = cliparse.parse_items([ # headers self.key_value_type('foo\\:bar:baz'), self.key_value_type('jack\\@jill:hill'), @@ -372,7 +373,7 @@ class ItemParsingTest(BaseTestCase): self.assertIn('bar@baz', files) def test_escape_longsep(self): - headers, data, files = cliparse.parse_items([ + headers, data, files, queries = cliparse.parse_items([ self.key_value_type('bob\\:==foo'), ]) self.assertDictEqual(data, { @@ -380,7 +381,7 @@ class ItemParsingTest(BaseTestCase): }) def test_valid_items(self): - headers, data, files = cliparse.parse_items([ + headers, data, files, queries = cliparse.parse_items([ self.key_value_type('string=value'), self.key_value_type('header:value'), self.key_value_type('list:=["a", 1, {}, false]'), @@ -389,6 +390,7 @@ class ItemParsingTest(BaseTestCase): self.key_value_type('ed='), self.key_value_type('bool:=true'), self.key_value_type('test-file@%s' % TEST_FILE_PATH), + self.key_value_type('query=:value'), ]) self.assertDictEqual(headers, { 'header': 'value', @@ -401,8 +403,19 @@ class ItemParsingTest(BaseTestCase): "list": ["a", 1, {}, False], "obj": {"a": "b"} }) + self.assertDictEqual(queries, { + 'query': 'value', + }) 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): From 76a3125153a000831ca49f73ebe40099cabbb425 Mon Sep 17 00:00:00 2001 From: Jake Basile Date: Wed, 18 Jul 2012 21:16:33 -0500 Subject: [PATCH 3/3] Updated documentation for query string params. --- README.rst | 16 +++++++++++++--- httpie/cli.py | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 1c08542e..128dcf67 100644 --- a/README.rst +++ b/README.rst @@ -54,7 +54,7 @@ Synopsis:: http [flags] [METHOD] URL [items] -There are four types of key-value pair items available: +There are five types of key-value pair items available: Headers (``Name:Value``) Arbitrary HTTP headers. The ``:`` character is used to separate a header's @@ -77,6 +77,9 @@ File fields (``field@/path/to/file``) ``screenshot@/path/to/file.png``. The presence of a file field results into a ``multipart/form-data`` request. +Query String Parameters (``name=:value``) + Appends the given name/value pair as a query string to the URL. + Examples ^^^^^^^^ @@ -118,6 +121,12 @@ The above will send the same request as if the following HTML form were submitte +Query string parameters can be added to any request:: + + http GET example.com/ search=:donuts + +Will GET the URL "example.com/?search=donuts". + A whole request body can be passed in via **``stdin``** instead, in which case it will be used with no further processing:: @@ -172,8 +181,9 @@ See ``http -h`` for more details:: include one. ITEM A key-value pair whose type is defined by the separator used. It can be an HTTP header - (header:value), a data field to be used in the request - body (field_name=value), a raw JSON data field + (header:value), a query parameter (name=:value), + a data field to be used in the request body + (field_name=value), a raw JSON data field (field_name:=value), or a file field (field_name@/path/to/file). You can use a backslash to escape a colliding separator in the field name. diff --git a/httpie/cli.py b/httpie/cli.py index 3449bf63..b152f0ad 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -212,7 +212,8 @@ parser.add_argument( A key-value pair whose type is defined by the separator used. It can be an HTTP header (header: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), or a file field (field_name@/path/to/file). You can use a backslash to escape a colliding separator in the field name.