From 9eac18015f03ed616e0e60dff2a905469df48385 Mon Sep 17 00:00:00 2001 From: Vinay Kumar Date: Thu, 6 Aug 2026 10:56:41 +0530 Subject: [PATCH] Fix inconsistent argument parsing behavior across Python versions This change modifies the argument parser to use `parse_intermixed_args()` instead of `parse_known_args()` to ensure consistent behavior across Python versions. This addresses issue #1838 where the position of optional flags like `-v` affects argument parsing differently depending on the Python version. The fix ensures that optional arguments can be placed before or after positional arguments consistently across all supported Python versions (3.11, 3.12, and 3.13+). --- httpie/cli/argparser.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/httpie/cli/argparser.py b/httpie/cli/argparser.py index 9bf09b3b73..c35db6fcd9 100644 --- a/httpie/cli/argparser.py +++ b/httpie/cli/argparser.py @@ -45,7 +45,7 @@ def __init__(self, max_help_position=6, *args, **kwargs): super().__init__(*args, **kwargs) def _split_lines(self, text, width): - text = dedent(text).strip() + '\n\n' + text = dedent(text).strip() + '\\n\\n' return text.splitlines() def add_usage(self, usage, actions, groups, prefix=None): @@ -72,7 +72,7 @@ def add_usage(self, usage, actions, groups, prefix=None): usage, displayed_actions, groups, - prefix="usage:\n " + prefix="usage:\\n " ) @@ -94,7 +94,7 @@ def parse_args( namespace=None ) -> argparse.Namespace: self.env = env - self.args, no_options = self.parse_known_args(args, namespace) + self.args, no_options = self.parse_intermixed_args(args, namespace) if self.args.debug: self.args.traceback = True self.has_stdin_data = ( @@ -126,9 +126,9 @@ def _print_message(self, message, file=None): class HTTPieManagerArgumentParser(BaseHTTPieArgumentParser): - def parse_known_args(self, args=None, namespace=None): + def parse_intermixed_args(self, args=None, namespace=None): try: - return super().parse_known_args(args, namespace) + return super().parse_intermixed_args(args, namespace) except SystemExit as exc: if not hasattr(self, 'root') and exc.code == 2: # Argument Parser Error raise argparse.ArgumentError(None, None) @@ -156,7 +156,7 @@ def parse_args( ) -> argparse.Namespace: self.env = env self.env.args = namespace = namespace or argparse.Namespace() - self.args, no_options = super().parse_known_args(args, namespace) + self.args, no_options = super().parse_intermixed_args(args, namespace) if self.args.debug: self.args.traceback = True self.has_stdin_data = ( @@ -213,7 +213,7 @@ def _process_url(self): scheme = self.args.default_scheme + '://' # See if we're using curl style shorthand for localhost (:3000/foo) - shorthand = re.match(r'^:(?!:)(\d*)(/?.*)$', self.args.url) + shorthand = re.match(r'^:(?!:)(\\d*)(/?.*)$', self.args.url) if shorthand: port = shorthand.group(1) rest = shorthand.group(2) @@ -306,7 +306,7 @@ def _process_auth(self): if (not self.args.ignore_netrc and self.args.auth is None and plugin.netrc_parse): - # Only host needed, so it’s OK URL not finalized. + # Only host needed, so it's OK URL not finalized. netrc_credentials = get_netrc_auth(self.args.url) if netrc_credentials: self.args.auth = AuthCredentials( @@ -377,7 +377,7 @@ def _apply_no_options(self, no_options): invalid.append(option) if invalid: - self.error(f'unrecognized arguments: {" ".join(invalid)}') + self.error(f'unrecognized arguments: {\" \".join(invalid)}') def _body_from_file(self, fd): """Read the data from a file-like object. @@ -591,7 +591,7 @@ def print_usage(self, file): whitelist.add(exception.args[0].option_strings[0]) usage_text = Text('usage', style='bold') - usage_text.append(':\n ') + usage_text.append(':\\n ') usage_text.append(rich_help.to_usage(self.spec, whitelist=whitelist)) self.env.rich_error_console.print(usage_text) @@ -610,4 +610,4 @@ def error(self, message): '''.rstrip() ) ) - self.exit(2) + self.exit(2) \ No newline at end of file