You've already forked httpie-cli
							
							
				mirror of
				https://github.com/httpie/cli.git
				synced 2025-10-30 23:47:52 +02:00 
			
		
		
		
	Default to POST also when stdin redirected.
+clean up
This commit is contained in:
		| @@ -18,12 +18,6 @@ TYPE_JSON = 'application/json; charset=utf-8' | ||||
|  | ||||
| def _get_response(parser, args, stdin, stdin_isatty): | ||||
|  | ||||
|     if not stdin_isatty: | ||||
|         if args.data: | ||||
|             parser.error('Request body (stdin) and request ' | ||||
|                                 'data (key=value) cannot be mixed.') | ||||
|         args.data = stdin.read() | ||||
|  | ||||
|     if args.json or (not args.form and args.data): | ||||
|         # JSON | ||||
|         if not args.files and ( | ||||
| @@ -114,7 +108,11 @@ def main(args=None, | ||||
|          stdin=sys.stdin, stdin_isatty=sys.stdin.isatty(), | ||||
|          stdout=sys.stdout, stdout_isatty=sys.stdout.isatty()): | ||||
|     parser = cli.parser | ||||
|     args = parser.parse_args(args if args is not None else sys.argv[1:]) | ||||
|     args = parser.parse_args( | ||||
|         args=args if args is not None else sys.argv[1:], | ||||
|         stdin=stdin, | ||||
|         stdin_isatty=stdin_isatty | ||||
|     ) | ||||
|     response = _get_response(parser, args, stdin, stdin_isatty) | ||||
|     output = _get_output(args, stdout_isatty, response) | ||||
|     output_bytes = output.encode('utf8') | ||||
|   | ||||
| @@ -176,9 +176,8 @@ 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 simple field or JSON data field | ||||
|         or file field presents then method is POST otherwise it is GET. | ||||
|         If this argument is omitted then httpie will guess the HTTP method. | ||||
|         If there is any data to be sent then method is POST otherwise it is GET. | ||||
|     ''') | ||||
| ) | ||||
| parser.add_argument( | ||||
|   | ||||
| @@ -3,6 +3,7 @@ CLI argument parsing logic. | ||||
|  | ||||
| """ | ||||
| import os | ||||
| import sys | ||||
| import re | ||||
| import json | ||||
| import argparse | ||||
| @@ -24,6 +25,11 @@ SEP_HEADERS = SEP_COMMON | ||||
| SEP_DATA = '=' | ||||
| SEP_DATA_RAW_JSON = ':=' | ||||
| SEP_FILES = '@' | ||||
| DATA_ITEM_SEPARATORS = { | ||||
|     SEP_DATA, | ||||
|     SEP_DATA_RAW_JSON, | ||||
|     SEP_FILES | ||||
| } | ||||
|  | ||||
|  | ||||
| OUT_REQ_HEADERS = 'H' | ||||
| @@ -43,15 +49,25 @@ DEFAULT_UA = 'HTTPie/%s' % __version__ | ||||
|  | ||||
| class HTTPieArgumentParser(argparse.ArgumentParser): | ||||
|  | ||||
|     def parse_args(self, args=None, namespace=None): | ||||
|     def parse_args(self, args=None, namespace=None, | ||||
|                    stdin=sys.stdin, | ||||
|                    stdin_isatty=sys.stdin.isatty()): | ||||
|         args = super(HTTPieArgumentParser, self).parse_args(args, namespace) | ||||
|         self._validate_output_options(args) | ||||
|         self._validate_auth_options(args) | ||||
|         self.suggest_method(args) | ||||
|         self._guess_method(args, stdin_isatty) | ||||
|         self._parse_items(args) | ||||
|         if not stdin_isatty: | ||||
|             self._process_stdin(args, stdin) | ||||
|         return args | ||||
|  | ||||
|     def suggest_method(self, args): | ||||
|     def _process_stdin(self, args, stdin): | ||||
|         if args.data: | ||||
|             self.error('Request body (stdin) and request ' | ||||
|                        'data (key=value) cannot be mixed.') | ||||
|         args.data = stdin.read() | ||||
|  | ||||
|     def _guess_method(self, args, stdin_isatty=sys.stdin.isatty()): | ||||
|         """Suggests HTTP method by positional argument values. | ||||
|  | ||||
|         In following description by data item it means one of: | ||||
| @@ -74,28 +90,47 @@ class HTTPieArgumentParser(argparse.ArgumentParser): | ||||
|  | ||||
|         The first argument should be treated as method | ||||
|         if it matches ^[a-zA-Z]+$ regexp. Otherwise it is url. | ||||
|  | ||||
|         """ | ||||
|         if args.method is None: | ||||
|             # Invoked as `http URL'. | ||||
|             assert not args.items | ||||
|             args.method = 'GET' | ||||
|             if not stdin_isatty: | ||||
|                 args.method = 'POST' | ||||
|             else: | ||||
|                 args.method = 'GET' | ||||
|         # FIXME: False positive, e.g., "localhost" matches but is a valid URL. | ||||
|         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. | ||||
|             # Invoked as `http URL item+': | ||||
|             # - The URL is now in `args.method`. | ||||
|             # - The first item is now in `args.url`. | ||||
|             # | ||||
|             # So we need to: | ||||
|             # - Guess the HTTP method. | ||||
|             # - Set `args.url` correctly. | ||||
|             # - Parse the first item and move it to `args.items[0]`. | ||||
|  | ||||
|             item = KeyValueType( | ||||
|                 SEP_COMMON, | ||||
|                 SEP_DATA, | ||||
|                 SEP_DATA_RAW_JSON, | ||||
|                 SEP_FILES | ||||
|             )(args.url) | ||||
|                 SEP_FILES).__call__(args.url) | ||||
|  | ||||
|             args.url = args.method | ||||
|             args.items.insert(0, item) | ||||
|             # Check if any data item presents | ||||
|             if any(item[2] in (SEP_DATA, SEP_DATA_RAW_JSON, SEP_FILES) for item in args.items): | ||||
|  | ||||
|             has_data = not stdin_isatty or any( | ||||
|                 item.sep in DATA_ITEM_SEPARATORS for item in args.items) | ||||
|             if has_data: | ||||
|                 args.method = 'POST' | ||||
|             else: | ||||
|                 args.method = 'GET' | ||||
|  | ||||
|     def _parse_items(self, args): | ||||
|         """ | ||||
|         Parse `args.items` into `args.headers`, `args.data` and `args.files`. | ||||
|  | ||||
|         """ | ||||
|         args.headers = CaseInsensitiveDict() | ||||
|         args.headers['User-Agent'] = DEFAULT_UA | ||||
|         args.data = OrderedDict() | ||||
|   | ||||
| @@ -6,68 +6,68 @@ from httpie.cliparse import HTTPieArgumentParser, KeyValue | ||||
| __author__ = 'vladimir' | ||||
|  | ||||
|  | ||||
|  | ||||
| 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() | ||||
|         self.parser = HTTPieArgumentParser() | ||||
|  | ||||
|     def test_suggest_when_method_set_and_valid(self): | ||||
|     def test_guess_when_method_set_and_valid(self): | ||||
|         args = Namespace() | ||||
|         args.method = 'GET' | ||||
|         args.url = 'http://example.com/' | ||||
|         args.items = [] | ||||
|  | ||||
|         self.httpie_argument_parser.suggest_method(args) | ||||
|         self.parser._guess_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): | ||||
|     def test_guess_when_method_not_set(self): | ||||
|         args = Namespace() | ||||
|         args.method = None | ||||
|         args.url = 'http://example.com/' | ||||
|         args.items = [] | ||||
|  | ||||
|         self.httpie_argument_parser.suggest_method(args) | ||||
|         self.parser._guess_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): | ||||
|     def test_guess_when_method_set_but_invalid_and_data_field(self): | ||||
|         args = Namespace() | ||||
|         args.method = 'http://example.com/' | ||||
|         args.url = 'data=field' | ||||
|         args.items = [] | ||||
|  | ||||
|         self.httpie_argument_parser.suggest_method(args) | ||||
|         self.parser._guess_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): | ||||
|     def test_guess_when_method_set_but_invalid_and_header_field(self): | ||||
|         args = Namespace() | ||||
|         args.method = 'http://example.com/' | ||||
|         args.url = 'test:header' | ||||
|         args.items = [] | ||||
|  | ||||
|         self.httpie_argument_parser.suggest_method(args) | ||||
|         self.parser._guess_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): | ||||
|     def test_guess_when_method_set_but_invalid_and_item_exists(self): | ||||
|         args = Namespace() | ||||
|         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.parser._guess_method(args) | ||||
|  | ||||
|         self.assertEquals(args.items, [ | ||||
|             KeyValue(key='new_item', value='a', sep='=', orig='new_item=a'), | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import tempfile | ||||
|  | ||||
| TESTS_ROOT = os.path.dirname(__file__) | ||||
| sys.path.insert(0, os.path.realpath(os.path.join(TESTS_ROOT, '..'))) | ||||
|  | ||||
| from httpie import __main__ | ||||
| from httpie import cliparse | ||||
|  | ||||
| @@ -145,39 +146,31 @@ class TestHTTPie(BaseTest): | ||||
|         self.assertIn('"Foo": "bar"', response) | ||||
|  | ||||
|  | ||||
| class TestImplicitHTTPMethod(BaseTest): | ||||
|  | ||||
| class TestHTTPieSuggestion(BaseTest): | ||||
|     def test_get(self): | ||||
|         http('http://httpbin.org/get') | ||||
|     def test_implicit_GET(self): | ||||
|         r = http('http://httpbin.org/get') | ||||
|         self.assertIn('HTTP/1.1 200', r) | ||||
|  | ||||
|     def test_post(self): | ||||
|     def test_implicit_GET_with_headers(self): | ||||
|         r = http('http://httpbin.org/headers', 'Foo:bar') | ||||
|         self.assertIn('"Foo": "bar"', r) | ||||
|         self.assertIn('HTTP/1.1 200', r) | ||||
|  | ||||
|     def test_implicit_POST_json(self): | ||||
|         r = http('http://httpbin.org/post', 'hello=world') | ||||
|         self.assertIn('"hello": "world"', r) | ||||
|         self.assertIn('HTTP/1.1 200', r) | ||||
|  | ||||
|     def test_verbose(self): | ||||
|         r = http('--verbose', 'http://httpbin.org/get', 'test-header:__test__') | ||||
|         self.assertEqual(r.count('__test__'), 2) | ||||
|     def test_implicit_POST_form(self): | ||||
|         r = http('--form', 'http://httpbin.org/post', 'foo=bar') | ||||
|         self.assertIn('"foo": "bar"', r) | ||||
|         self.assertIn('HTTP/1.1 200', r) | ||||
|  | ||||
|     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) | ||||
|     def test_implicit_POST_stdin(self): | ||||
|         r = http('--form', 'http://httpbin.org/post', | ||||
|                  stdin=open(TEST_FILE), stdin_isatty=False) | ||||
|         self.assertIn('HTTP/1.1 200', r) | ||||
|  | ||||
|  | ||||
| class TestPrettyFlag(BaseTest): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user