summaryrefslogtreecommitdiff
path: root/docs/narr.rst
blob: dc88bda8280a2169b5f3aa64d6dd51b1fc2fc950 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
:mod:`zope.proxy` Narrative Documentation
=========================================

Subclassing :class:`ProxyBase`
------------------------------

If you subclass a proxy, instances of the subclass have access to
data defined in the class, including descriptors.

Your subclass instances don't get instance dictionaries, but they
can have slots.

.. doctest::

   >>> from zope.proxy import ProxyBase
   >>> class MyProxy(ProxyBase):
   ...    __slots__ = 'x', 'y'
   ...
   ...    def f(self):
   ...        return self.x

   >>> l = [1, 2, 3]
   >>> p = MyProxy(l)

You can use attributes defined by the class, including slots:

.. doctest::

   >>> p.x = 'x'
   >>> p.x
   'x'
   >>> p.f()
   'x'

You can also use attributes of the proxied object:

.. doctest::

   >>> p
   [1, 2, 3]
   >>> p.pop()
   3
   >>> p
   [1, 2]


Using get descriptors in proxy classes
--------------------------------------

A non-data descriptor in a proxy class doesn't hide an attribute on
a proxied object or prevent writing the attribute.

.. doctest::

    >>> class ReadDescr(object):
    ...     def __get__(self, i, c):
    ...         return 'read'

    >>> from zope.proxy import ProxyBase
    >>> class MyProxy(ProxyBase):
    ...    __slots__ = ()
    ...
    ...    z = ReadDescr()
    ...    q = ReadDescr()

    >>> class MyOb:
    ...    q = 1

    >>> o = MyOb()
    >>> p = MyProxy(o)
    >>> p.q
    1

    >>> p.z
    'read'

    >>> p.z = 1
    >>> o.z, p.z
    (1, 1)


Marking proxy attributes as non-overridable
-------------------------------------------

Normally, methods defined in proxies are overridden by
methods of proxied objects.  This applies to all non-data
descriptors.  The non_overridable function can be used to
convert a non-data descriptor to a data descriptor that disallows
writes.  This function can be used as a decorator to make functions
defined in proxy classes take precedence over functions defined
in proxied objects.

.. doctest::

   >>> from zope.proxy import ProxyBase
   >>> from zope.proxy import non_overridable
   >>> class MyProxy(ProxyBase):
   ...    __slots__ = ()
   ...
   ...    @non_overridable
   ...    def foo(self):
   ...        return 'MyProxy foo'

   >>> class MyOb:
   ...    def foo(self):
   ...        return 'MyOb foo'

   >>> o = MyOb()
   >>> p = MyProxy(o)
   >>> p.foo()
   'MyProxy foo'


Changing the proxied object
---------------------------

.. doctest::

   >>> from zope.proxy import ProxyBase
   >>> from zope.proxy import setProxiedObject, getProxiedObject

   >>> class C(object):
   ...     pass

   >>> c1 = C()
   >>> c2 = C()

   >>> p = ProxyBase(c1)

`setProxiedObject()` allows us to change the object a proxy refers to,
returning the previous referent:

.. doctest::

   >>> old = setProxiedObject(p, c2)
   >>> old is c1
   True

   >>> getProxiedObject(p) is c2
   True

The first argument  to `setProxiedObject()` must be a proxy; other objects
cause it to raise an exception:

.. doctest::

   >>> try:
   ...     setProxiedObject(c1, None)
   ... except TypeError:
   ...     print("TypeError raised")
   ... else:
   ...     print("Expected TypeError not raised")
   TypeError raised