diff --git a/httpie/compat.py b/httpie/compat.py index bb222d91..45ccfe7a 100644 --- a/httpie/compat.py +++ b/httpie/compat.py @@ -3,28 +3,28 @@ Python 2.6, 2.7, and 3.x compatibility. """ # Borrow these from requests: -#noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences from requests.compat import is_windows, bytes, str, is_py3, is_py26 try: - #noinspection PyUnresolvedReferences,PyCompatibility + # noinspection PyUnresolvedReferences,PyCompatibility from urllib.parse import urlsplit except ImportError: - #noinspection PyUnresolvedReferences,PyCompatibility + # noinspection PyUnresolvedReferences,PyCompatibility from urlparse import urlsplit try: - #noinspection PyCompatibility + # noinspection PyCompatibility from urllib.request import urlopen except ImportError: - #noinspection PyCompatibility + # noinspection PyCompatibility from urllib2 import urlopen try: from collections import OrderedDict except ImportError: - ### Python 2.6 OrderedDict class, needed for headers, parameters, etc .### - ### + # Python 2.6 OrderedDict class, needed for headers, parameters, etc .### + # # noinspection PyCompatibility from UserDict import DictMixin diff --git a/httpie/input.py b/httpie/input.py index 19bdd904..ff0bf034 100644 --- a/httpie/input.py +++ b/httpie/input.py @@ -4,13 +4,12 @@ import os import sys import re -import json import errno import mimetypes import getpass from io import BytesIO from collections import namedtuple -#noinspection PyCompatibility +# noinspection PyCompatibility from argparse import ArgumentParser, ArgumentTypeError, ArgumentError # TODO: Use MultiDict for headers once added to `requests`. @@ -19,6 +18,7 @@ from requests.structures import CaseInsensitiveDict from httpie.compat import OrderedDict, urlsplit, str from httpie.sessions import VALID_SESSION_NAME_PATTERN +from httpie.utils import load_json_preserve_order HTTP_POST = 'POST' @@ -111,7 +111,7 @@ class Parser(ArgumentParser): kwargs['add_help'] = False super(Parser, self).__init__(*args, **kwargs) - #noinspection PyMethodOverriding + # noinspection PyMethodOverriding def parse_args(self, env, args=None, namespace=None): self.env = env @@ -640,7 +640,7 @@ def parse_items(items, if item.sep in SEP_GROUP_RAW_JSON_ITEMS: try: - value = json.loads(value, object_pairs_hook=OrderedDict) + value = load_json_preserve_order(value) except ValueError as e: raise ParseError('"%s": %s' % (item.orig, e)) target = data diff --git a/httpie/utils.py b/httpie/utils.py index 0989372c..ad2119aa 100644 --- a/httpie/utils.py +++ b/httpie/utils.py @@ -1,4 +1,13 @@ from __future__ import division +import json + +from httpie.compat import is_py26, OrderedDict + + +def load_json_preserve_order(s): + if is_py26: + return json.loads(s) + return json.loads(s, object_pairs_hook=OrderedDict) def humanize_bytes(n, precision=2): diff --git a/tests/test_httpie.py b/tests/test_httpie.py index 0dd0e9cc..92cb1932 100644 --- a/tests/test_httpie.py +++ b/tests/test_httpie.py @@ -1,7 +1,10 @@ """High-level tests.""" +import pytest from utils import TestEnvironment, http, HTTP_OK from fixtures import FILE_PATH, FILE_CONTENT + import httpie +from httpie.compat import is_py26 class TestHTTPie: @@ -64,7 +67,13 @@ class TestHTTPie: assert '"User-Agent": "HTTPie' in r, r assert '"Foo": "bar"' in r - def test_json_order(self, httpbin): - r = http('PATCH', httpbin.url + '/patch', 'order:={"map":{"1":"first","2":"second"}}') + @pytest.mark.skipif( + is_py26, + reason='the `object_pairs_hook` arg for `json.loads()` is Py>2.6 only' + ) + def test_json_input_preserve_order(self, httpbin): + r = http('PATCH', httpbin.url + '/patch', + 'order:={"map":{"1":"first","2":"second"}}') assert HTTP_OK in r - assert r.json['data'] == '{"order": {"map": {"1": "first", "2": "second"}}}' + assert r.json['data'] == \ + '{"order": {"map": {"1": "first", "2": "second"}}}'