=======================
 zope.security.checker
=======================

.. currentmodule:: zope.security.checker


Module API Documentation
========================

.. automodule:: zope.security.checker


API Doctests
============

Protections for Modules
-----------------------

The :func:`moduleChecker` API can be used to determine whether a
module has been protected: Initially, there's no checker defined for
the module:

.. doctest::

   >>> from zope.security.checker import moduleChecker
   >>> from zope.security.tests import test_zcml_functest
   >>> moduleChecker(test_zcml_functest) is None
   True

We can add a checker using
:func:`zope.security.metaconfigure.protectModule` (although this is
more commonly done using ZCML):

.. doctest::

   >>> from zope.component import provideUtility
   >>> from zope.security.metaconfigure import protectModule
   >>> from zope.security.permission import Permission
   >>> from zope.security.interfaces import IPermission
   >>> TEST_PERM = 'zope.security.metaconfigure.test'
   >>> perm = Permission(TEST_PERM, '')
   >>> provideUtility(perm, IPermission, TEST_PERM)
   >>> protectModule(test_zcml_functest, 'foo', TEST_PERM)

Now, the checker should exist and have an access dictionary with the
name and permission:

.. doctest::

   >>> def pprint(ob, width=70):
   ...     from pprint import PrettyPrinter
   ...     PrettyPrinter(width=width).pprint(ob)
   >>> checker = moduleChecker(test_zcml_functest)
   >>> cdict = checker.get_permissions
   >>> pprint(cdict)
   {'foo': 'zope.security.metaconfigure.test'}

   If we define additional names, they will be added to the dict:

   >>> protectModule(test_zcml_functest, 'bar', TEST_PERM)
   >>> protectModule(test_zcml_functest, 'baz', TEST_PERM)
   >>> pprint(cdict)
   {'bar': 'zope.security.metaconfigure.test',
    'baz': 'zope.security.metaconfigure.test',
    'foo': 'zope.security.metaconfigure.test'}

The allow directive creates actions for each name defined
directly, or via interface:

.. doctest::

   >>> from zope.interface import Interface
   >>> from zope.interface import Attribute
   >>> from zope.security.metaconfigure import allow
   >>> class I1(Interface):
   ...     def x(): pass
   ...     y = Attribute("Y")
   >>> class I2(I1):
   ...     def a(): pass
   ...     b = Attribute("B")
   >>> class AContext(object):
   ...     def __init__(self):
   ...         self.actions = []
   ...
   ...     def action(self, discriminator, callable, args):
   ...         self.actions.append(
   ...             {'discriminator': discriminator,
   ...              'callable': int(callable is protectModule),
   ...              'args': args})
   ...     module='testmodule'

   >>> context = AContext()
   >>> allow(context, attributes=['foo', 'bar'], interface=[I1, I2])
   >>> context.actions.sort(key=lambda a: a['discriminator'])
   >>> pprint(context.actions)
   [{'args': ('testmodule', 'a', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'a')},
    {'args': ('testmodule', 'b', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'b')},
    {'args': ('testmodule', 'bar', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'bar')},
    {'args': ('testmodule', 'foo', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'foo')},
    {'args': ('testmodule', 'x', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'x')},
    {'args': ('testmodule', 'y', 'zope.Public'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'y')}]

The provide directive creates actions for each name defined
directly, or via interface:

.. doctest::

   >>> from zope.security.metaconfigure import require
   >>> class RContext(object):
   ...     def __init__(self):
   ...         self.actions = []
   ...     def action(self, discriminator, callable, args):
   ...         self.actions.append(
   ...             {'discriminator': discriminator,
   ...              'callable': int(callable is protectModule),
   ...              'args': args})
   ...     module='testmodule'

   >>> context = RContext()
   >>> require(context, attributes=['foo', 'bar'],
   ...         interface=[I1, I2], permission='p')

   >>> context.actions.sort(key=lambda a: a['discriminator'])
   >>> pprint(context.actions)
   [{'args': ('testmodule', 'a', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'a')},
    {'args': ('testmodule', 'b', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'b')},
    {'args': ('testmodule', 'bar', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'bar')},
    {'args': ('testmodule', 'foo', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'foo')},
    {'args': ('testmodule', 'x', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'x')},
    {'args': ('testmodule', 'y', 'p'),
     'callable': 1,
     'discriminator': ('http://namespaces.zope.org/zope:module',
                       'testmodule',
                       'y')}]


Protections for standard objects
--------------------------------

.. doctest::

   >>> from zope.security.checker import ProxyFactory
   >>> from zope.security.interfaces import ForbiddenAttribute
   >>> def check_forbidden_get(object, attr):
   ...     from zope.security.interfaces import ForbiddenAttribute
   ...     try:
   ...         return getattr(object, attr)
   ...     except ForbiddenAttribute as e:
   ...         return 'ForbiddenAttribute: %s' % e.args[0]
   >>> def check_forbidden_setitem(object, item, value):
   ...     from zope.security.interfaces import ForbiddenAttribute
   ...     try:
   ...         object[item] = value
   ...     except ForbiddenAttribute as e:
   ...         return 'ForbiddenAttribute: %s' % e.args[0]
   >>> def check_forbidden_delitem(object, item):
   ...     from zope.security.interfaces import ForbiddenAttribute
   ...     try:
   ...         del object[item]
   ...     except ForbiddenAttribute as e:
   ...         return 'ForbiddenAttribute: %s' % e.args[0]
   >>> def check_forbidden_call(callable, *args): # **
   ...     from zope.security.interfaces import ForbiddenAttribute
   ...     try:
   ...         return callable(*args) # **
   ...     except ForbiddenAttribute as e:
   ...         return 'ForbiddenAttribute: %s' % e.args[0]

Rocks
~~~~~

Rocks are immutable, non-callable objects without interesting methods.  They
*don't* get proxied.

.. doctest::

   >>> type(ProxyFactory(object())) is object
   True
   >>> type(ProxyFactory(1)) is int
   True
   >>> type(ProxyFactory(1.0)) is float
   True
   >>> type(ProxyFactory(1j)) is complex
   True
   >>> type(ProxyFactory(None)) is type(None)
   True
   >>> type(ProxyFactory('xxx')) is str
   True
   >>> type(ProxyFactory(True)) is type(True)
   True

Datetime-related instances are rocks, too:

.. doctest::

   >>> from datetime import timedelta, datetime, date, time, tzinfo
   >>> type(ProxyFactory(  timedelta(1)  )) is timedelta
   True
   >>> type(ProxyFactory(  datetime(2000, 1, 1)  )) is datetime
   True
   >>> type(ProxyFactory(  date(2000, 1, 1)  )) is date
   True
   >>> type(ProxyFactory(  time()  )) is time
   True
   >>> type(ProxyFactory(  tzinfo() )) is tzinfo
   True
   >>> try:
   ...     from pytz import UTC
   ... except ImportError:  # pytz checker only if pytz is present.
   ...     True
   ... else:
   ...      type(ProxyFactory(  UTC )) is type(UTC)
   True


dicts
~~~~~

We can do everything we expect to be able to do with proxied dicts.

.. doctest::

   >>> d = ProxyFactory({'a': 1, 'b': 2})
   >>> check_forbidden_get(d, 'clear') # Verify that we are protected
   'ForbiddenAttribute: clear'
   >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected
   'ForbiddenAttribute: __setitem__'
   >>> d['a']
   1
   >>> len(d)
   2
   >>> sorted(list(d))
   ['a', 'b']
   >>> d.get('a')
   1
   >>> 'a' in d
   True
   >>> c = d.copy()
   >>> check_forbidden_get(c, 'clear')
   'ForbiddenAttribute: clear'
   >>> str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")
   True
   >>> repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")
   True
   >>> def sorted(x):
   ...    x = list(x)
   ...    x.sort()
   ...    return x
   >>> sorted(d.keys())
   ['a', 'b']
   >>> sorted(d.values())
   [1, 2]
   >>> sorted(d.items())
   [('a', 1), ('b', 2)]

Always available (note, that dicts in python-3.x are not orderable, so we are
not checking that under python > 2):

.. doctest::

    >>> d != d
    False
    >>> bool(d)
    True
    >>> d.__class__ == dict
    True

lists
~~~~~

We can do everything we expect to be able to do with proxied lists.

.. doctest::

   >>> l = ProxyFactory([1, 2])
   >>> check_forbidden_delitem(l, 0)
   'ForbiddenAttribute: __delitem__'
   >>> check_forbidden_setitem(l, 0, 3)
   'ForbiddenAttribute: __setitem__'
   >>> l[0]
   1
   >>> l[0:1]
   [1]
   >>> check_forbidden_setitem(l[:1], 0, 2)
   'ForbiddenAttribute: __setitem__'
   >>> len(l)
   2
   >>> tuple(l)
   (1, 2)
   >>> 1 in l
   True
   >>> l.index(2)
   1
   >>> l.count(2)
   1
   >>> str(l)
   '[1, 2]'
   >>> repr(l)
   '[1, 2]'
   >>> l + l
   [1, 2, 1, 2]

Always available:

.. doctest::

   >>> l < l
   False
   >>> l > l
   False
   >>> l <= l
   True
   >>> l >= l
   True
   >>> l == l
   True
   >>> l != l
   False
   >>> bool(l)
   True
   >>> l.__class__ == list
   True

tuples
~~~~~~

We can do everything we expect to be able to do with proxied tuples.

.. doctest::

   >>> from zope.security.checker import ProxyFactory
   >>> l = ProxyFactory((1, 2))
   >>> l[0]
   1
   >>> l[0:1]
   (1,)
   >>> len(l)
   2
   >>> list(l)
   [1, 2]
   >>> 1 in l
   True
   >>> str(l)
   '(1, 2)'
   >>> repr(l)
   '(1, 2)'
   >>> l + l
   (1, 2, 1, 2)

Always available:

.. doctest::

   >>> l < l
   False
   >>> l > l
   False
   >>> l <= l
   True
   >>> l >= l
   True
   >>> l == l
   True
   >>> l != l
   False
   >>> bool(l)
   True
   >>> l.__class__ == tuple
   True

sets
~~~~

we can do everything we expect to be able to do with proxied sets.

.. doctest::

   >>> us = set((1, 2))
   >>> s = ProxyFactory(us)

   >>> check_forbidden_get(s, 'add') # Verify that we are protected
   'ForbiddenAttribute: add'
   >>> check_forbidden_get(s, 'remove') # Verify that we are protected
   'ForbiddenAttribute: remove'
   >>> check_forbidden_get(s, 'discard') # Verify that we are protected
   'ForbiddenAttribute: discard'
   >>> check_forbidden_get(s, 'pop') # Verify that we are protected
   'ForbiddenAttribute: pop'
   >>> check_forbidden_get(s, 'clear') # Verify that we are protected
   'ForbiddenAttribute: clear'

   >>> len(s)
   2

   >>> 1 in s
   True

   >>> 1 not in s
   False

   >>> s.issubset(set((1,2,3)))
   True

   >>> s.issuperset(set((1,2,3)))
   False

   >>> c = s.union(set((2, 3)))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s | set((2, 3))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s | ProxyFactory(set((2, 3)))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = set((2, 3)) | s
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.intersection(set((2, 3)))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s & set((2, 3))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s & ProxyFactory(set((2, 3)))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = set((2, 3)) & s
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.difference(set((2, 3)))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s - ProxyFactory(set((2, 3)))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s - set((2, 3))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = set((2, 3)) - s
   >>> sorted(c)
   [3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.symmetric_difference(set((2, 3)))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s ^ set((2, 3))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s ^ ProxyFactory(set((2, 3)))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = set((2, 3)) ^ s
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.copy()
   >>> sorted(c)
   [1, 2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> str(s) == str(us)
   True

   >>> repr(s) == repr(us)
   True

   Always available:

   >>> s < us
   False
   >>> s > us
   False
   >>> s <= us
   True
   >>> s >= us
   True
   >>> s == us
   True
   >>> s != us
   False

Note that you can't compare proxied sets with other proxied sets
due to a limitation in the set comparison functions which won't work
with any kind of proxy.

.. doctest::

   >>> bool(s)
   True
   >>> s.__class__ == set
   True



frozensets
~~~~~~~~~~

we can do everything we expect to be able to do with proxied frozensets.

.. doctest::

   >>> def check_forbidden_get(object, attr):
   ...     from zope.security.interfaces import ForbiddenAttribute
   ...     try:
   ...         return getattr(object, attr)
   ...     except ForbiddenAttribute as e:
   ...         return 'ForbiddenAttribute: %s' % e.args[0]
   >>> from zope.security.checker import ProxyFactory
   >>> from zope.security.interfaces import ForbiddenAttribute
   >>> us = frozenset((1, 2))
   >>> s = ProxyFactory(us)

   >>> check_forbidden_get(s, 'add') # Verify that we are protected
   'ForbiddenAttribute: add'
   >>> check_forbidden_get(s, 'remove') # Verify that we are protected
   'ForbiddenAttribute: remove'
   >>> check_forbidden_get(s, 'discard') # Verify that we are protected
   'ForbiddenAttribute: discard'
   >>> check_forbidden_get(s, 'pop') # Verify that we are protected
   'ForbiddenAttribute: pop'
   >>> check_forbidden_get(s, 'clear') # Verify that we are protected
   'ForbiddenAttribute: clear'

   >>> len(s)
   2

   >>> 1 in s
   True

   >>> 1 not in s
   False

   >>> s.issubset(frozenset((1,2,3)))
   True

   >>> s.issuperset(frozenset((1,2,3)))
   False

   >>> c = s.union(frozenset((2, 3)))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s | frozenset((2, 3))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s | ProxyFactory(frozenset((2, 3)))
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = frozenset((2, 3)) | s
   >>> sorted(c)
   [1, 2, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.intersection(frozenset((2, 3)))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s & frozenset((2, 3))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s & ProxyFactory(frozenset((2, 3)))
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = frozenset((2, 3)) & s
   >>> sorted(c)
   [2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.difference(frozenset((2, 3)))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s - ProxyFactory(frozenset((2, 3)))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s - frozenset((2, 3))
   >>> sorted(c)
   [1]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = frozenset((2, 3)) - s
   >>> sorted(c)
   [3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.symmetric_difference(frozenset((2, 3)))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s ^ frozenset((2, 3))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s ^ ProxyFactory(frozenset((2, 3)))
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = frozenset((2, 3)) ^ s
   >>> sorted(c)
   [1, 3]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> c = s.copy()
   >>> sorted(c)
   [1, 2]
   >>> check_forbidden_get(c, 'add')
   'ForbiddenAttribute: add'

   >>> str(s) == str(us)
   True

   >>> repr(s) == repr(us)
   True

   Always available:

   >>> s < us
   False
   >>> s > us
   False
   >>> s <= us
   True
   >>> s >= us
   True
   >>> s == us
   True
   >>> s != us
   False

Note that you can't compare proxied sets with other proxied sets
due to a limitation in the frozenset comparison functions which won't work
with any kind of proxy.

.. doctest::

   >>> bool(s)
   True
   >>> s.__class__ == frozenset
   True

iterators
~~~~~~~~~

.. doctest::

   >>> [a for a in ProxyFactory(iter([1, 2]))]
   [1, 2]
   >>> list(ProxyFactory(iter([1, 2])))
   [1, 2]
   >>> list(ProxyFactory(iter((1, 2))))
   [1, 2]
   >>> list(ProxyFactory(iter({1:1, 2:2})))
   [1, 2]
   >>> def f():
   ...     for i in 1, 2:
   ...             yield i
   ...
   >>> list(ProxyFactory(f()))
   [1, 2]
   >>> list(ProxyFactory(f)())
   [1, 2]


We can iterate over custom sequences, too:

.. doctest::

   >>> class X(object):
   ...   d = 1, 2, 3
   ...   def __getitem__(self, i):
   ...      return self.d[i]
   ...
   >>> x = X()

We can iterate over sequences

.. doctest::

   >>> list(x)
   [1, 2, 3]

   >>> from zope.security.checker import NamesChecker
   >>> from zope.security.checker import ProxyFactory
   >>> c = NamesChecker(['__getitem__', '__len__'])
   >>> p = ProxyFactory(x, c)

Even if they are proxied

.. doctest::

   >>> list(p)
   [1, 2, 3]

But if the class has an iter:

.. doctest::

   >>> X.__iter__ = lambda self: iter(self.d)
   >>> list(x)
   [1, 2, 3]

We shouldn't be able to iterate if we don't have an assertion:

.. doctest::

   >>> check_forbidden_call(list, p)
   'ForbiddenAttribute: __iter__'


New-style classes
~~~~~~~~~~~~~~~~~

.. doctest::

   >>> from zope.security.checker import NamesChecker
   >>> class C(object):
   ...    x = 1
   ...    y = 2
   >>> C = ProxyFactory(C)
   >>> check_forbidden_call(C)
   'ForbiddenAttribute: __call__'
   >>> check_forbidden_get(C, '__dict__')
   'ForbiddenAttribute: __dict__'
   >>> s = str(C)
   >>> s = repr(C)
   >>> C.__module__ == __name__
   True
   >>> len(C.__bases__)
   1
   >>> len(C.__mro__)
   2

Always available:

.. doctest::

   >>> C == C
   True
   >>> C != C
   False
   >>> bool(C)
   True
   >>> C.__class__ == type
   True

New-style Instances
~~~~~~~~~~~~~~~~~~~

.. doctest::

   >>> class C(object):
   ...    x = 1
   ...    y = 2
   >>> c = ProxyFactory(C(), NamesChecker(['x']))
   >>> check_forbidden_get(c, 'y')
   'ForbiddenAttribute: y'
   >>> check_forbidden_get(c, 'z')
   'ForbiddenAttribute: z'
   >>> c.x
   1
   >>> c.__class__ == C
   True

Always available:

.. doctest::

   >>> c == c
   True
   >>> c != c
   False
   >>> bool(c)
   True
   >>> c.__class__ == C
   True


Classic Classes
~~~~~~~~~~~~~~~

.. doctest::

   >>> class C:
   ...    x = 1
   >>> C = ProxyFactory(C)
   >>> check_forbidden_call(C)
   'ForbiddenAttribute: __call__'
   >>> check_forbidden_get(C, '__dict__')
   'ForbiddenAttribute: __dict__'
   >>> s = str(C)
   >>> s = repr(C)
   >>> C.__module__ == __name__
   True

Note that these are really only classic on Python 2:

   >>> import sys
   >>> len(C.__bases__) == (0 if sys.version_info[0] == 2 else 1)
   True

Always available:

.. doctest::

   >>> C == C
   True
   >>> C != C
   False
   >>> bool(C)
   True

Classic Instances
~~~~~~~~~~~~~~~~~

.. doctest::

   >>> class C(object):
   ...    x, y = 1, 2
   >>> c = ProxyFactory(C(), NamesChecker(['x']))
   >>> check_forbidden_get(c, 'y')
   'ForbiddenAttribute: y'
   >>> check_forbidden_get(c, 'z')
   'ForbiddenAttribute: z'
   >>> c.x
   1
   >>> c.__class__ == C
   True

Always available:

.. doctest::

   >>> c == c
   True
   >>> c != c
   False
   >>> bool(c)
   True
   >>> c.__class__ == C
   True

Interfaces and declarations
~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can still use interfaces though proxies:

.. doctest::

   >>> from zope.interface import directlyProvides
   >>> from zope.interface import implementer
   >>> from zope.interface import provider
   >>> class I(Interface):
   ...     pass
   >>> class IN(Interface):
   ...     pass
   >>> class II(Interface):
   ...     pass
   >>> @implementer(I)
   ... @provider(IN)
   ... class N(object):
   ...     pass
   >>> n = N()
   >>> directlyProvides(n, II)
   >>> N = ProxyFactory(N)
   >>> n = ProxyFactory(n)
   >>> I.implementedBy(N)
   True
   >>> IN.providedBy(N)
   True
   >>> I.providedBy(n)
   True
   >>> II.providedBy(n)
   True


Abstract Base Classes
~~~~~~~~~~~~~~~~~~~~~

We work with the ABCMeta meta class:

.. doctest::

   >>> import abc
   >>> MyABC = abc.ABCMeta('MyABC', (object,), {})
   >>> class Foo(MyABC): pass
   >>> class Bar(Foo): pass
   >>> PBar = ProxyFactory(Bar)
   >>> [c.__name__ for c in PBar.__mro__]
   ['Bar', 'Foo', 'MyABC', 'object']
   >>> check_forbidden_call(PBar)
   'ForbiddenAttribute: __call__'
   >>> check_forbidden_get(PBar, '__dict__')
   'ForbiddenAttribute: __dict__'
   >>> s = str(PBar)
   >>> s = repr(PBar)
   >>> PBar.__module__ == __name__
   True
   >>> len(PBar.__bases__)
   1

Always available:

.. doctest::

   >>> PBar == PBar
   True
   >>> PBar != PBar
   False
   >>> bool(PBar)
   True
   >>> PBar.__class__ == type
   False
