diff options
author | Jason Madden <jamadden@gmail.com> | 2017-09-15 07:43:30 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2017-09-15 07:43:30 -0500 |
commit | 502c31eea599977b20c43d8659db09f71ed0cff2 (patch) | |
tree | d1504f83cf6315c4546067e608a954d196ccdc12 /docs | |
parent | 8b93f2bb08c45c18f207f61b6691fd13664f8609 (diff) | |
download | zope-security-502c31eea599977b20c43d8659db09f71ed0cff2.tar.gz |
Expand the proxy issues section on isinstance.
Diffstat (limited to 'docs')
-rw-r--r-- | docs/proxy.rst | 86 |
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 ~~~~~~~ |