Skip to content

Commit

Permalink
[BUGFIX] Move unescape parsing to separate function (#1108)
Browse files Browse the repository at this point in the history
Fixes #1107 
```
FunctionRuntimeException: Runtime error occurred when executing the function %from_json: Invalid control character
```
  • Loading branch information
jmbannon authored Oct 27, 2024
1 parent bf64be8 commit 6a756cf
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 18 deletions.
18 changes: 18 additions & 0 deletions docs/source/config_reference/scripting/scripting_functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,24 @@ titlecase
:description:
Capitalize each word in the string.

unescape
~~~~~~~~
:spec: ``unescape(string: String) -> String``

:description:
Unescape symbols like newlines or tabs to their true form.

:usage:

.. code-block:: python
{
%unescape( "Hello\nWorld" )
}
# Hello
# World
upper
~~~~~
:spec: ``upper(string: String) -> String``
Expand Down
19 changes: 19 additions & 0 deletions src/ytdl_sub/script/functions/string_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,22 @@ def pad_zero(numeric: Numeric, length: Integer) -> String:
length=length,
char=String("0"),
)

@staticmethod
def unescape(string: String) -> String:
"""
:description:
Unescape symbols like newlines or tabs to their true form.
:usage:
.. code-block:: python
{
%unescape( "Hello\\nWorld" )
}
# Hello
# World
"""
return String(string.value.encode("utf-8").decode("unicode_escape"))
12 changes: 2 additions & 10 deletions src/ytdl_sub/script/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,16 +301,8 @@ def _parse_string(self, str_open_token: str) -> String:
self._pos += len(str_open_token)
return String(value=string_value)

# Read literal "\n" as newlines
if self._read(increment_pos=False, length=2) == "\\n":
string_value += "\n"
self._pos += 2
elif self._read(increment_pos=False, length=2) == "\\t":
string_value += "\t"
self._pos += 2
else:
self._pos += 1
string_value += ch
self._pos += 1
string_value += ch

raise STRINGS_NOT_CLOSED

Expand Down
12 changes: 6 additions & 6 deletions tests/unit/script/functions/test_string_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,18 @@ def test_contains_any(self, value, expected_output):
@pytest.mark.parametrize(
"input_string, split, max_split, expected_output",
[
("no splits", " | ", None, ["no splits"]),
("one | split", " | ", None, ["one", "split"]),
("max | split | one", " | ", 1, ["max", "split | one"]),
("multiline\ndescription", "\\n", None, ["multiline", "description"]),
("no splits", "' | '", None, ["no splits"]),
("one | split", "' | '", None, ["one", "split"]),
("max | split | one", "' | '", 1, ["max", "split | one"]),
("multiline\ndescription", "%unescape('\\n')", None, ["multiline", "description"]),
],
)
def test_split(
self, input_string: str, split: str, max_split: Optional[int], expected_output: List[str]
):
if max_split:
output = single_variable_output(f"{{%split('{input_string}', '{split}', {max_split})}}")
output = single_variable_output(f"{{%split('{input_string}', {split}, {max_split})}}")
else:
output = single_variable_output(f"{{%split('{input_string}', '{split}')}}")
output = single_variable_output(f"{{%split('{input_string}', {split})}}")

assert output == expected_output
12 changes: 10 additions & 2 deletions tests/unit/script/types/test_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,22 @@ def test_string_not_arg(self, string: str):
("{%string('backslash \\\\')}", "backslash \\\\"),
("{%string('''triple quote with \" ' \\''')}", "triple quote with \" ' \\"),
('{%string("""triple quote with " \' \\""")}', "triple quote with \" ' \\"),
("{%string('literal \\n newlines')}", "literal \n newlines"),
("{%string('supports \t tabs')}", "supports \t tabs"),
("{%string('literal \\t tabs')}", "literal \t tabs"),
],
)
def test_string(self, string: str, expected_string: str):
assert single_variable_output(string) == expected_string

@pytest.mark.parametrize(
"string, expected_string",
[
("{%unescape('literal \\n newlines')}", "literal \n newlines"),
("{%unescape('literal \\t tabs')}", "literal \t tabs"),
],
)
def test_unescape(self, string: str, expected_string: str):
assert single_variable_output(string) == expected_string

def test_null_is_empty_string(self):
assert Script({"out": "{%string(null)}"}).resolve() == ScriptOutput({"out": String("")})

Expand Down

0 comments on commit 6a756cf

Please sign in to comment.