diff --git a/CHANGES.rst b/CHANGES.rst index 1ea6f62..cc45628 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog 5.2 (unreleased) ---------------- +- Prevent builtins shadowing in DTML Var. + (`Zope#1285 `_) + 5.1 (2026-01-19) ---------------- diff --git a/src/DocumentTemplate/DT_Util.py b/src/DocumentTemplate/DT_Util.py index 9b43eb2..63ebbc9 100644 --- a/src/DocumentTemplate/DT_Util.py +++ b/src/DocumentTemplate/DT_Util.py @@ -204,6 +204,9 @@ def eval(self, md): d.update(self.globals) for name in self.used: __traceback_info__ = name + # Don't try to override builtins, since they need to be protected + if d.get("__builtins__") and name in d["__builtins__"]: + continue try: if name not in d: d[name] = md.getitem(name, 0) diff --git a/src/DocumentTemplate/tests/testDTML.py b/src/DocumentTemplate/tests/testDTML.py index ab8ee11..3596034 100644 --- a/src/DocumentTemplate/tests/testDTML.py +++ b/src/DocumentTemplate/tests/testDTML.py @@ -408,6 +408,46 @@ def y(self): ''')(i=C()) self.assertEqual(res, expected) + def test_subitem_with_builtins_names(self): + # Test builtins names shadowing + import Acquisition + from ExtensionClass import Base + + class C(Base, Acquisition.Implicit): + __allow_access_to_unprotected_subobjects__ = 1 + + def __init__(self, x): + self.x = x + + def __call__(self, *args, **kwargs): + return f"rendering: {self.x}" + + def __len__(self): + return len(self.x) + + foo = C("foo") + foo.str = bar = C("bar") + foo.len = baz = C("baz") + expected = ( + ''' + foo, + foo, + bar, + rendering: foo, + rendering: bar, + 3, + rendering: baz''') + res = self.doc_class( + ''' + , + , + , + , + , + , + ''')(**{"foo": foo, "str": bar, "len": baz}) + self.assertEqual(res, expected) + def testWith(self): class person: __allow_access_to_unprotected_subobjects__ = 1