diff --git a/src/python_minifier/token_printer.py b/src/python_minifier/token_printer.py index 2bd22b2..60ff367 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 0000000..fd9c15e --- /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("\'\\"\'")'