1
0
mirror of https://github.com/httpie/cli.git synced 2025-02-03 13:01:58 +02:00

Issue #54 Method suggestion proposal

This commit is contained in:
Vladimir Berkutov 2012-06-17 21:46:56 +04:00
parent 78fff98712
commit bca36f0464
6 changed files with 160 additions and 13 deletions

View File

@ -34,11 +34,11 @@ Usage
Hello world::
http GET httpie.org
http httpie.org
Synopsis::
http [flags] METHOD URL [items]
http [flags] [METHOD] URL [items]
There are four types of key-value pair items available:
@ -115,7 +115,10 @@ Most of the flags mirror the arguments understood by ``requests.request``. See `
positional arguments:
METHOD The HTTP method to be used for the request (GET, POST,
PUT, DELETE, PATCH, ...).
PUT, DELETE, PATCH, ...). If this argument is omitted
then httpie will guess HTTP method. If there is either
form data field or JSON data field or file field
presents then method is POST otherwise it is GET.
URL The protocol defaults to http:// if the URL does not
include one.
ITEM A key-value pair whose type is defined by the

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
import re
import sys
import json
@ -41,12 +40,6 @@ def _get_response(parser, args, stdin, stdin_isatty):
# Form
args.headers['Content-Type'] = TYPE_FORM
if args.method is None and not args.items:
args.method = 'GET'
elif not re.match('^[a-zA-Z]+$', args.method):
args.items.insert(0, args.url)
args.method, args.url = 'POST', args.method
# Fire the request.
try:
credentials = None

View File

@ -176,6 +176,9 @@ parser.add_argument(
help=_('''
The HTTP method to be used for the request
(GET, POST, PUT, DELETE, PATCH, ...).
If this argument is omitted then httpie will guess HTTP method.
If there is either form data field or JSON data field
or file field presents then method is POST otherwise it is GET.
''')
)
parser.add_argument(

View File

@ -47,9 +47,54 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
args = super(HTTPieArgumentParser, self).parse_args(args, namespace)
self._validate_output_options(args)
self._validate_auth_options(args)
self.suggest_method(args)
self._parse_items(args)
return args
def suggest_method(self, args):
"""Suggests HTTP method by positional argument values.
In following description by data item it means one of:
* form data item (key=value)
* JSON raw item (key:=value)
* file item (key@value)
If METHOD argument is omitted and no data ITEM is given then method is GET:
http http://example.com/
- is shortcut for -
http GET http://example.com.
If METHOD argument is omitted but at least one data ITEM
is present then method is POST:
http http://example.com/ hello=world
- is shortcut for -
http POST http://example.com hello=world.
If METHOD is specified then http behaves as it is now.
The first argument should be treated as method
if it matches ^[a-zA-Z]+$ regexp. Otherwise it is url.
"""
if args.method is None:
assert not args.items
args.method = 'GET'
elif not re.match('^[a-zA-Z]+$', args.method):
# If first position argument is not http method going guessing mode.
# The second positional argument (if any) definitely must be an item.
item = KeyValueType(
SEP_COMMON,
SEP_DATA,
SEP_DATA_RAW_JSON,
SEP_FILES
)(args.url)
args.url = args.method
args.items.insert(0, item)
# Check if any data item presents
if any(item[2] in ('=', ':=', '@') for item in args.items):
args.method = 'POST'
else:
args.method = 'GET'
def _parse_items(self, args):
args.headers = CaseInsensitiveDict()
args.headers['User-Agent'] = DEFAULT_UA

74
tests/test_cliparse.py Normal file
View File

@ -0,0 +1,74 @@
from httpie.cliparse import HTTPieArgumentParser, KeyValue
from mock import Mock
__author__ = 'vladimir'
import unittest
class HTTPieArgumentParserTestCase(unittest.TestCase):
def setUp(self):
self.HTTPieArgumentParserStub = type(HTTPieArgumentParser.__name__, (HTTPieArgumentParser,), {})
self.HTTPieArgumentParserStub.__init__ = lambda self: None
self.httpie_argument_parser = self.HTTPieArgumentParserStub()
def test_suggest_when_method_set_and_valid(self):
args = Mock()
args.method = 'GET'
args.url = 'http://example.com/'
args.items = []
self.httpie_argument_parser.suggest_method(args)
self.assertEquals(args.method, 'GET')
self.assertEquals(args.url, 'http://example.com/')
self.assertEquals(args.items, [])
def test_suggest_when_method_not_set(self):
args = Mock()
args.method = None
args.url = 'http://example.com/'
args.items = []
self.httpie_argument_parser.suggest_method(args)
self.assertEquals(args.method, 'GET')
self.assertEquals(args.url, 'http://example.com/')
self.assertEquals(args.items, [])
def test_suggest_when_method_set_but_invalid_and_data_field(self):
args = Mock()
args.method = 'http://example.com/'
args.url = 'data=field'
args.items = []
self.httpie_argument_parser.suggest_method(args)
self.assertEquals(args.method, 'POST')
self.assertEquals(args.url, 'http://example.com/')
self.assertEquals(args.items, [KeyValue(key='data', value='field', sep='=', orig='data=field')])
def test_suggest_when_method_set_but_invalid_and_header_field(self):
args = Mock()
args.method = 'http://example.com/'
args.url = 'test:header'
args.items = []
self.httpie_argument_parser.suggest_method(args)
self.assertEquals(args.method, 'GET')
self.assertEquals(args.url, 'http://example.com/')
self.assertEquals(args.items, [KeyValue(key='test', value='header', sep=':', orig='test:header')])
def test_suggest_when_method_set_but_invalid_and_item_exists(self):
args = Mock()
args.method = 'http://example.com/'
args.url = 'new_item=a'
args.items = [KeyValue(key='old_item', value='b', sep='=', orig='old_item=b')]
self.httpie_argument_parser.suggest_method(args)
self.assertEquals(args.items, [
KeyValue(key='new_item', value='a', sep='=', orig='new_item=a'),
KeyValue(key='old_item', value='b', sep='=', orig='old_item=b'),
])

View File

@ -144,11 +144,40 @@ class TestHTTPie(BaseTest):
self.assertIn('"User-Agent": "HTTPie', response)
self.assertIn('"Foo": "bar"', response)
def test_get_suggestion(self):
class TestHTTPieSuggestion(BaseTest):
def test_get(self):
http('http://httpbin.org/get')
def test_post_suggestion(self):
http('http://httpbin.org/post', 'hello=world')
def test_post(self):
r = http('http://httpbin.org/post', 'hello=world')
self.assertIn('"hello": "world"', r)
def test_verbose(self):
r = http('--verbose', 'http://httpbin.org/get', 'test-header:__test__')
self.assertEqual(r.count('__test__'), 2)
def test_verbose_form(self):
r = http('--verbose', '--form', 'http://httpbin.org/post', 'foo=bar', 'baz=bar')
self.assertIn('foo=bar&baz=bar', r)
def test_json(self):
response = http('http://httpbin.org/post', 'foo=bar')
self.assertIn('"foo": "bar"', response)
response2 = http('-j', 'GET', 'http://httpbin.org/headers')
self.assertIn('"Accept": "application/json"', response2)
response3 = http('-j', 'GET', 'http://httpbin.org/headers', 'Accept:application/xml')
self.assertIn('"Accept": "application/xml"', response3)
def test_form(self):
response = http('--form', 'http://httpbin.org/post', 'foo=bar')
self.assertIn('"foo": "bar"', response)
def test_headers(self):
response = http('http://httpbin.org/headers', 'Foo:bar')
self.assertIn('"User-Agent": "HTTPie', response)
self.assertIn('"Foo": "bar"', response)
class TestPrettyFlag(BaseTest):