Skip to content

Commit

Permalink
Added escaping of special characters in typing::Literal
Browse files Browse the repository at this point in the history
  • Loading branch information
timohl committed Jan 9, 2025
1 parent 1b8873b commit e0e2225
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
5 changes: 5 additions & 0 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,11 @@ class cpp_function : public function {
} else {
signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
}
} else if (c == '!'
&& (*(pc + 1) == '!' || *(pc + 1) == '@' || *(pc + 1) == '%'
|| *(pc + 1) == '{' || *(pc + 1) == '}')) {
// typing::Literal escapes special characters with !
signature += *++pc;
} else if (c == '@') {
// `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see
// typing::Callable/detail::arg_descr/detail::return_descr)
Expand Down
39 changes: 35 additions & 4 deletions include/pybind11/typing.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,36 @@ struct StringLiteral {
char name[N];
};

template <StringLiteral str>
consteval auto sanitize_string_literal() {
constexpr std::string_view v(str.name);
char result[v.size() + std::ranges::count(v, '!') + std::ranges::count(v, '@')
+ std::ranges::count(v, '%') + std::ranges::count(v, '{')
+ std::ranges::count(v, '}') + 1];
size_t i = 0;
for (auto c : str.name) {
if (c == '!') {
result[i++] = '!';
result[i++] = '!';
} else if (c == '@') {
result[i++] = '!';
result[i++] = '@';
} else if (c == '%') {
result[i++] = '!';
result[i++] = '%';
} else if (c == '{') {
result[i++] = '!';
result[i++] = '{';
} else if (c == '}') {
result[i++] = '!';
result[i++] = '}';
} else {
result[i++] = c;
}
}
return StringLiteral(result);
}

template <StringLiteral... StrLits>
class Literal : public object {
PYBIND11_OBJECT_DEFAULT(Literal, object, PyObject_Type)
Expand Down Expand Up @@ -253,13 +283,14 @@ struct handle_type_name<typing::Never> {
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
template <typing::StringLiteral... Literals>
struct handle_type_name<typing::Literal<Literals...>> {
static constexpr auto name = const_name("Literal[")
+ pybind11::detail::concat(const_name(Literals.name)...)
+ const_name("]");
static constexpr auto name
= const_name("Literal[")
+ pybind11::detail::concat(const_name(sanitize_string_literal<Literals>().name)...)
+ const_name("]");
};
template <typing::StringLiteral StrLit>
struct handle_type_name<typing::TypeVar<StrLit>> {
static constexpr auto name = const_name(StrLit.name);
static constexpr auto name = const_name(sanitize_string_literal<StrLit>().name);
};
#endif

Expand Down

0 comments on commit e0e2225

Please sign in to comment.