diff --git a/httpie/cli/argtypes.py b/httpie/cli/argtypes.py index 8f19c3c51e..760f0c8564 100644 --- a/httpie/cli/argtypes.py +++ b/httpie/cli/argtypes.py @@ -225,9 +225,20 @@ def parse_format_options(s: str, defaults: Optional[dict]) -> dict: if value in value_map: parsed_value = value_map[value] else: - if value.isnumeric(): + # Use a strict int conversion rather than value.isnumeric(): + # isnumeric() accepts unicode numerics like '½', '²', and + # Arabic-Indic digits like '٠'/'١' that int() cannot parse, + # and a Unicode value that did happen to round-trip through + # int() (Arabic-Indic) was inconsistent across Python builds + # and would still produce an option whose type didn't match + # the default_type check below when the default was a plain + # int. Falling back to int() with a try/except means anything + # isnumeric() let through but int() can't decode now raises + # a clear ArgumentTypeError naming the offending token, and + # the type-mismatch branch catches the rest. + try: parsed_value = int(value) - else: + except ValueError: parsed_value = value if defaults is None: diff --git a/tests/test_output.py b/tests/test_output.py index 2242177dbc..378fe4d9b2 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -426,6 +426,10 @@ def test_json_formatting_options(self, options: str, expected_json: str): ({'foo': {'bar': 1}}, 'foo.bar:2', {'foo': {'bar': 2}}), ({'foo': {'bar': True}}, 'foo.bar:false', {'foo': {'bar': False}}), ({'foo': {'bar': 'a'}}, 'foo.bar:b', {'foo': {'bar': 'b'}}), + # int() accepts a leading minus, so a negative integer should + # round-trip through the same try/except int(value) branch. + ({'foo': {'bar': 1}}, 'foo.bar:-2', {'foo': {'bar': -2}}), + ({'foo': {'bar': 0}}, 'foo.bar:-1', {'foo': {'bar': -1}}), # @formatter:on ] ) @@ -439,6 +443,9 @@ def test_parse_format_options(self, defaults, options_string, expected): ('foo:2', 'invalid option'), ('foo.baz:2', 'invalid key'), ('foo.bar:false', 'expected int got bool'), + ('١٢٣:2', 'invalid option'), + ('٤٥٦:2', 'invalid option'), + ('٧٨٩:2', 'invalid option'), ] ) def test_parse_format_options_errors(self, options_string, expected_error):