From a0fdf0194a2e65e09edb7c62490f02cdbc952bb7 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Thu, 5 Oct 2023 16:34:33 +0200 Subject: [PATCH 1/5] fix: Escape paths with spaces in include list from --includes --- pybind11/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pybind11/__main__.py b/pybind11/__main__.py index b656ce6fee..17cea34284 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -2,6 +2,7 @@ from __future__ import annotations import argparse +import shlex import sys import sysconfig @@ -22,7 +23,7 @@ def print_includes() -> None: if d and d not in unique_dirs: unique_dirs.append(d) - print(" ".join("-I" + d for d in unique_dirs)) + print(" ".join(shlex.quote("-I" + d) for d in unique_dirs)) def main() -> None: From 4c9413a97b8103068b58055103ba9957c73f276f Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Thu, 5 Oct 2023 22:32:17 +0200 Subject: [PATCH 2/5] fix: --includes should not use shlex on Windows platforms --- pybind11/__main__.py | 15 +++++++++++++-- pyproject.toml | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 17cea34284..65282c50dd 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -2,13 +2,24 @@ from __future__ import annotations import argparse -import shlex import sys import sysconfig from ._version import __version__ from .commands import get_cmake_dir, get_include, get_pkgconfig_dir +try: + from oslex import quote +except ImportError: + import os + + if os.name != "nt": + from shlex import quote + else: + # minimal attempt, handling only the most common case (spaces in path) + def quote(s: str) -> str: + return '"' + s + '"' if " " in s else s + def print_includes() -> None: dirs = [ @@ -23,7 +34,7 @@ def print_includes() -> None: if d and d not in unique_dirs: unique_dirs.append(d) - print(" ".join(shlex.quote("-I" + d) for d in unique_dirs)) + print(" ".join(quote("-I" + d) for d in unique_dirs)) def main() -> None: diff --git a/pyproject.toml b/pyproject.toml index c5e2651d6a..8b294b2012 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true [[tool.mypy.overrides]] -module = ["ghapi.*"] +module = ["ghapi.*", "oslex"] ignore_missing_imports = true From 95b1bed5abbd3da9c751071829de6537e813f30a Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 31 Oct 2023 22:55:52 -0400 Subject: [PATCH 3/5] Apply suggestions from code review --- pybind11/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 65282c50dd..e93478739e 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -18,7 +18,7 @@ else: # minimal attempt, handling only the most common case (spaces in path) def quote(s: str) -> str: - return '"' + s + '"' if " " in s else s + return f'"{s}"' if " " in s else s def print_includes() -> None: @@ -34,7 +34,7 @@ def print_includes() -> None: if d and d not in unique_dirs: unique_dirs.append(d) - print(" ".join(quote("-I" + d) for d in unique_dirs)) + print(" ".join(quote(f"-I{d}") for d in unique_dirs)) def main() -> None: From c516c1b17cf82f6821e6a02c661da564986740d1 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 14 Aug 2024 14:57:26 -0400 Subject: [PATCH 4/5] fix: use custom impl Signed-off-by: Henry Schreiner --- pybind11/__main__.py | 35 +++++++++++++++++++++++------------ pyproject.toml | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pybind11/__main__.py b/pybind11/__main__.py index e93478739e..134129c940 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -2,23 +2,34 @@ from __future__ import annotations import argparse +import re import sys import sysconfig from ._version import __version__ from .commands import get_cmake_dir, get_include, get_pkgconfig_dir -try: - from oslex import quote -except ImportError: - import os +# This is the conditional used for os.path being posixpath +if "posix" in sys.builtin_module_names: + from shlex import quote +elif "nt" in sys.builtin_module_names: + # See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121 + # and the original documents: + # https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and + # https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ + UNSAFE = re.compile("[ \t\n\r]") - if os.name != "nt": - from shlex import quote - else: - # minimal attempt, handling only the most common case (spaces in path) - def quote(s: str) -> str: - return f'"{s}"' if " " in s else s + def quote(s: str) -> str: + if s and not UNSAFE.search(s): + return s + + # Paths cannot contain a '"' on Windows, so we don't need to worry + # about nuanced counting here. + return f'"{s}"' +else: + + def quote(s: str) -> str: + return s def print_includes() -> None: @@ -66,9 +77,9 @@ def main() -> None: if args.includes: print_includes() if args.cmakedir: - print(get_cmake_dir()) + print(quote(get_cmake_dir())) if args.pkgconfigdir: - print(get_pkgconfig_dir()) + print(quote(get_pkgconfig_dir())) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 8b294b2012..c5e2651d6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true [[tool.mypy.overrides]] -module = ["ghapi.*", "oslex"] +module = ["ghapi.*"] ignore_missing_imports = true From f38c9bcc881f7a0258e7b0f63b06d4b9a20cfb33 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Wed, 14 Aug 2024 22:11:27 +0200 Subject: [PATCH 5/5] Support trailing backslashes Co-authored-by: Henry Schreiner --- pybind11/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 134129c940..0abc7e2117 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -25,7 +25,7 @@ def quote(s: str) -> str: # Paths cannot contain a '"' on Windows, so we don't need to worry # about nuanced counting here. - return f'"{s}"' + return f'"{s}\\"' if s.endswith("\\") else f'"{s}"' else: def quote(s: str) -> str: