Skip to content

Commit

Permalink
fix: pass through unexpected format strings
Browse files Browse the repository at this point in the history
Uses a regex-based solution instead of str.format
  • Loading branch information
henryiii authored and joerick committed Oct 23, 2021
1 parent b488289 commit 97e3fbb
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
42 changes: 39 additions & 3 deletions cibuildwheel/util.py
Expand Up @@ -12,7 +12,7 @@
from enum import Enum
from pathlib import Path
from time import sleep
from typing import Dict, Iterator, List, Optional
from typing import Any, Dict, Iterator, List, Optional

import bracex
import certifi
Expand Down Expand Up @@ -48,14 +48,50 @@
)


def format_safe(template: str, **kwargs: Any) -> str:
"""
Works similarly to `template.format(**kwargs)`, except that unmatched
fields in `template` are passed through untouched.
>>> format_safe('{a} {b}', a='123')
'123 {b}'
>>> format_safe('{a} {b[4]:3f}', a='123')
'123 {b[4]:3f}'
To avoid variable expansion, precede with a single backslash e.g.
>>> format_safe('\\{a} {b}', a='123')
'{a} {b}'
"""

result = template

for key, value in kwargs.items():
find_pattern = re.compile(
fr"""
(?<!\\) # don't match if preceded by a backslash
{{ # literal open curly bracket
{re.escape(key)} # the field name
}} # literal close curly bracket
""",
re.VERBOSE,
)

result = re.sub(find_pattern, str(value), result)

# transform escaped sequences into their literal equivalents
result = result.replace(f"\\{{{key}}}", f"{{{key}}}")

return result


def prepare_command(command: str, **kwargs: PathOrStr) -> str:
"""
Preprocesses a command by expanding variables like {python}.
For example, used in the test_command option to specify the path to the
project's root.
project's root. Unmatched syntax will mostly be allowed through.
"""
return command.format(python="python", pip="pip", **kwargs)
return format_safe(command, python="python", pip="pip", **kwargs)


def get_build_verbosity_extra_flags(level: int) -> List[str]:
Expand Down
36 changes: 36 additions & 0 deletions unit_test/utils_test.py
@@ -0,0 +1,36 @@
from cibuildwheel.util import format_safe, prepare_command


def test_format_safe():
assert format_safe("{wheel}", wheel="filename.whl") == "filename.whl"
assert format_safe("command \\{wheel}", wheel="filename.whl") == "command {wheel}"
assert format_safe("{command \\{wheel}}", wheel="filename.whl") == "{command {wheel}}"

# check unmatched brackets
assert format_safe("{command {wheel}", wheel="filename.whl") == "{command filename.whl"

# check positional-style arguments i.e. {}
assert (
format_safe("find . -name * -exec ls -a {} \\;", project="/project")
== "find . -name * -exec ls -a {} \\;"
)


def test_prepare_command():
assert prepare_command("python -m {project}", project="project") == "python -m project"
assert prepare_command("python -m {something}", project="project") == "python -m {something}"
assert (
prepare_command("python -m {something.abc}", project="project")
== "python -m {something.abc}"
)

assert (
prepare_command("python -m {something.abc[4]:3f}", project="project")
== "python -m {something.abc[4]:3f}"
)

# test some unusual syntax that used to trip up the str.format approach
assert (
prepare_command("{a}{a,b}{b:.2e}{c}{d%s}{e:3}{f[0]}", a="42", b="3.14159")
== "42{a,b}{b:.2e}{c}{d%s}{e:3}{f[0]}"
)

0 comments on commit 97e3fbb

Please sign in to comment.