summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-09-15 07:43:30 -0500
committerJason Madden <jamadden@gmail.com>2017-09-15 07:43:30 -0500
commit502c31eea599977b20c43d8659db09f71ed0cff2 (patch)
treed1504f83cf6315c4546067e608a954d196ccdc12 /docs
parent8b93f2bb08c45c18f207f61b6691fd13664f8609 (diff)
downloadzope-security-502c31eea599977b20c43d8659db09f71ed0cff2.tar.gz
Expand the proxy issues section on isinstance.
Diffstat (limited to 'docs')
-rw-r--r--docs/proxy.rst86
1 files changed, 86 insertions, 0 deletions
diff --git a/docs/proxy.rst b/docs/proxy.rst
index 20cf3af..2b01ee0 100644
--- a/docs/proxy.rst
+++ b/docs/proxy.rst
@@ -227,6 +227,68 @@ Known Issues With Proxies
Security proxies (proxies in general) are not perfect in Python. There
are some things that they cannot transparently proxy.
+.. _isinstance-and-proxies:
+
+isinstance and proxies
+----------------------
+
+A proxied object cannot proxy its type (although it does proxy its ``__class__``):
+
+.. doctest::
+
+ >>> from zope.security.proxy import ProxyFactory
+ >>> class Object(object):
+ ... pass
+ >>> target = Object()
+ >>> target.__class__
+ <class 'Object'>
+ >>> type(target)
+ <class 'Object'>
+ >>> proxy = ProxyFactory(target, None)
+ >>> proxy.__class__
+ <class 'Object'>
+ >>> type(proxy)
+ <... 'zope.security...Proxy...'>
+
+This means that the builtin :func:`isinstance` may return unexpected
+results:
+
+.. doctest::
+
+ >>> isinstance(target, Object)
+ True
+ >>> isinstance(proxy, Object)
+ False
+
+There are two workarounds. The safest is to use
+:func:`zope.security.proxy.isinstance`, which takes specifically this
+into account (in modules that will be dealing with a number of
+proxies, it is common to simply place ``from zope.security.proxy
+import isinstance`` at the top of the file to override the builtin
+:func:`isinstance`; we won't show that here for clarity):
+
+.. doctest::
+
+ >>> import zope.security.proxy
+ >>> zope.security.proxy.isinstance(target, Object)
+ True
+ >>> zope.security.proxy.isinstance(proxy, Object)
+ True
+
+Alternatively, you can manually remove the security proxy (or indeed,
+all proxies) with :func:`zope.security.proxy.removeSecurityProxy` or
+:func:`zope.proxy.removeAllProxies`, respectively, before calling
+:func:`isinstance`:
+
+.. doctest::
+
+ >>> from zope.security.proxy import removeSecurityProxy
+ >>> isinstance(removeSecurityProxy(target), Object)
+ True
+ >>> isinstance(removeSecurityProxy(proxy), Object)
+ True
+
+
issubclass and proxies
----------------------
@@ -259,11 +321,35 @@ unexpectedly:
.. doctest::
>>> from collections import Mapping
+ >>> from abc import ABCMeta
+ >>> isinstance(Mapping, ABCMeta)
+ True
>>> isinstance(proxy, Mapping)
Traceback (most recent call last):
...
TypeError: issubclass() arg 1 must be a class
+
+In this case, the workarounds described :ref:`above <isinstance-and-proxies>` also work:
+
+.. doctest::
+
+ >>> zope.security.proxy.isinstance(proxy, Mapping)
+ False
+ >>> isinstance(removeSecurityProxy(proxy), Mapping)
+ False
+
+.. We need to clean up the caching that ABC does on
+.. pure-python platforms to make sure that we get where
+.. we expect to be when we construct LogRecord; otherwise
+.. the ProxyMetaclass may be in the negative cache, bypassing
+.. the issubclass() calls we expect
+
+.. doctest::
+ :hide:
+
+ >>> ABCMeta._abc_invalidation_counter += 1
+
logging
~~~~~~~