From 1ccc1e4860e2f375516be09993cd9b259d24c63a Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:50:37 -0700 Subject: [PATCH] Choose the shorter quote style for string literals repr() only switches to double quotes when a string contains a single quote and no double quote. A string containing both, with more single quotes than double quotes, was therefore rendered with every single quote escaped, which could make the output longer than the input. Try the double quoted form as well and keep whichever is shorter, still preferring single quotes on a tie. Fixes #150 --- src/python_minifier/token_printer.py | 27 ++++++++++++++++++++++++++- test/test_string_quotes.py | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/test_string_quotes.py diff --git a/src/python_minifier/token_printer.py b/src/python_minifier/token_printer.py index 2bd22b27..60ff3675 100644 --- a/src/python_minifier/token_printer.py +++ b/src/python_minifier/token_printer.py @@ -4,6 +4,31 @@ import sys +def _shortest_repr(value): + """ + The shortest repr of a str, preferring single quotes on a tie. + + repr() only switches to double quotes when the string contains a single + quote and no double quote, so a string containing more single quotes than + double quotes is rendered with every single quote escaped. Try the double + quoted form too and keep whichever is shorter. + """ + + s = repr(value) + + if "'" not in value or '"' not in value: + return s + + prefix = s[:s.index("'")] + body = s[len(prefix) + 1:-1] + double_quoted = prefix + '"' + body.replace("\\'", "'").replace('"', '\\"') + '"' + + if len(double_quoted) < len(s): + return double_quoted + + return s + + class TokenTypes(object): NoToken = 0 Identifier = 1 @@ -145,7 +170,7 @@ def keyword(self, kw): def stringliteral(self, value): """Add a string literal to the output code.""" - s = repr(value) + s = _shortest_repr(value) if sys.version_info < (3, 0) and self.unicode_literals: if s[0] == 'u': diff --git a/test/test_string_quotes.py b/test/test_string_quotes.py new file mode 100644 index 00000000..fd9c15e6 --- /dev/null +++ b/test/test_string_quotes.py @@ -0,0 +1,22 @@ +"""Tests for the quote style chosen for string literals.""" + +from python_minifier import minify + + +def test_no_quotes_uses_single_quotes(): + assert minify('print("")') == "print('')" + + +def test_single_quote_in_string_uses_double_quotes(): + assert minify('print(\'\\\'\')') == 'print("\'")' + + +def test_tie_prefers_single_quotes(): + assert minify('print("\'\\"")') == 'print(\'\\\'"\')' + + +def test_more_single_quotes_than_double_uses_double_quotes(): + # A string with more single quotes than double quotes must not be + # rendered with every single quote escaped, which made the output + # longer than the input. + assert minify('print("\'\\"\'")') == 'print("\'\\"\'")'