From 6ad2d1ee352aca0f80d814e5e615e4c74e535fd5 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 16 Aug 2024 06:51:26 +0100 Subject: [PATCH] Allow calling builtin_method objects by default (#112) The type of these objects is a subclass of `types.BuiltinMethodType`, but not identical to it. This caused downstream failures on Python >= 3.12 due to refactoring of the `io` module, such as https://github.com/zopefoundation/zope.file/issues/13. --- CHANGES.rst | 6 ++++++ src/zope/security/checker.py | 9 +++++++++ src/zope/security/tests/test_proxy.py | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5470a8b..edb4c29 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ 7.1 (unreleased) ---------------- +- Allow calling methods of type ```` by default. In + particular, Python 3.12 refactored the ``io`` module in such a way as to + slightly change the types of some methods, causing ``zope.security`` to no + longer consider them callable. See `zope.file issue #13 + `. + 7.0 (2024-05-30) ---------------- diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py index 72096bd..861317b 100644 --- a/src/zope/security/checker.py +++ b/src/zope/security/checker.py @@ -60,6 +60,7 @@ import abc import datetime import decimal +import io import os import sys import types @@ -879,6 +880,14 @@ def f(): # pragma: no cover types.MethodType: _callableChecker, types.BuiltinFunctionType: _callableChecker, types.BuiltinMethodType: _callableChecker, + # At least on Python 3.5-3.12, types.BuiltinFunctionType and + # types.BuiltinMethodType are both , + # or PyCFunctionType. However, some builtin methods are instead, or PyCMethodType, which is a subclass of + # PyCFunctionType but not identical to it. As of Python 3.12, the io + # module makes more use of PyCMethodType, so we can use it to identify + # that type. + type(io.BytesIO().getbuffer): _callableChecker, # method-wrapper type(().__repr__): _callableChecker, type: _typeChecker, diff --git a/src/zope/security/tests/test_proxy.py b/src/zope/security/tests/test_proxy.py index 90e9fc3..63fef19 100644 --- a/src/zope/security/tests/test_proxy.py +++ b/src/zope/security/tests/test_proxy.py @@ -13,6 +13,8 @@ ############################################################################## """Security proxy tests """ +import io +import os import unittest from zope.security._compat import PURE_PYTHON @@ -1815,6 +1817,11 @@ def test_method_wrapper(self): self.assertEqual(ProxyFactory({}).__repr__(), '{}') + def test_builtin_method(self): + from zope.security.proxy import ProxyFactory + + self.assertEqual(ProxyFactory(io.FileIO(os.devnull, 'rb').read)(), b'') + def test_using_mapping_slots_hack(): """The security proxy will use mapping slots, on the checker to go faster