Skip to content

Commit

Permalink
relax speedups str check (#477)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism authored Oct 17, 2024
2 parents 9c44ecf + 8cb1691 commit e85aff4
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 2 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Version 3.1.0
-------------

Unreleased

- Fix compatibility when ``__str__`` returns a ``str`` subclass. :issue:`472`


Version 3.0.1
-------------

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "MarkupSafe"
version = "3.0.1"
version = "3.1.0.dev"
description = "Safely add untrusted strings to HTML/XML markup."
readme = "README.md"
license = { file = "LICENSE.txt" }
Expand Down
2 changes: 1 addition & 1 deletion src/markupsafe/_speedups.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ escape_unicode_kind4(PyUnicodeObject *in)
static PyObject*
escape_unicode(PyObject *self, PyObject *s)
{
if (!PyUnicode_CheckExact(s))
if (!PyUnicode_Check(s))
return NULL;

// This check is no longer needed in Python 3.12.
Expand Down
36 changes: 36 additions & 0 deletions tests/test_escape.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import typing as t

import pytest

from markupsafe import escape
Expand Down Expand Up @@ -30,3 +32,37 @@
)
def test_escape(value: str, expect: str) -> None:
assert escape(value) == Markup(expect)


class Proxy:
def __init__(self, value: t.Any) -> None:
self.__value = value

@property # type: ignore[misc]
def __class__(self) -> type[t.Any]:
# Make o.__class__ and isinstance(o, str) see the proxied object.
return self.__value.__class__ # type: ignore[no-any-return]

def __str__(self) -> str:
return str(self.__value)


def test_proxy() -> None:
"""Handle a proxy object that pretends its __class__ is str."""
p = Proxy("test")
assert p.__class__ is str
assert isinstance(p, str)
assert escape(p) == Markup("test")


class ReferenceStr(str):
def __str__(self) -> str:
# This should return a str, but it returns the subclass instead.
return self


def test_subclass() -> None:
"""Handle if str(o) does not return a plain str."""
s = ReferenceStr("test")
assert isinstance(s, str)
assert escape(s) == Markup("test")

0 comments on commit e85aff4

Please sign in to comment.