Skip to content

Commit 8cb1691

Browse files
committed
relax speedups str check
1 parent 4dafb7c commit 8cb1691

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Version 3.1.0
33

44
Unreleased
55

6+
- Fix compatibility when ``__str__`` returns a ``str`` subclass. :issue:`472`
7+
68

79
Version 3.0.1
810
-------------

src/markupsafe/_speedups.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ escape_unicode_kind4(PyUnicodeObject *in)
151151
static PyObject*
152152
escape_unicode(PyObject *self, PyObject *s)
153153
{
154-
if (!PyUnicode_CheckExact(s))
154+
if (!PyUnicode_Check(s))
155155
return NULL;
156156

157157
// This check is no longer needed in Python 3.12.

tests/test_escape.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import typing as t
4+
35
import pytest
46

57
from markupsafe import escape
@@ -30,3 +32,37 @@
3032
)
3133
def test_escape(value: str, expect: str) -> None:
3234
assert escape(value) == Markup(expect)
35+
36+
37+
class Proxy:
38+
def __init__(self, value: t.Any) -> None:
39+
self.__value = value
40+
41+
@property # type: ignore[misc]
42+
def __class__(self) -> type[t.Any]:
43+
# Make o.__class__ and isinstance(o, str) see the proxied object.
44+
return self.__value.__class__ # type: ignore[no-any-return]
45+
46+
def __str__(self) -> str:
47+
return str(self.__value)
48+
49+
50+
def test_proxy() -> None:
51+
"""Handle a proxy object that pretends its __class__ is str."""
52+
p = Proxy("test")
53+
assert p.__class__ is str
54+
assert isinstance(p, str)
55+
assert escape(p) == Markup("test")
56+
57+
58+
class ReferenceStr(str):
59+
def __str__(self) -> str:
60+
# This should return a str, but it returns the subclass instead.
61+
return self
62+
63+
64+
def test_subclass() -> None:
65+
"""Handle if str(o) does not return a plain str."""
66+
s = ReferenceStr("test")
67+
assert isinstance(s, str)
68+
assert escape(s) == Markup("test")

0 commit comments

Comments
 (0)