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
|
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Location support borrowed from ``zope.location``, but without
``zope.security`` support, which is not used by ``repoze.bfg``
"""
import zope.interface
from repoze.bfg.interfaces import ILocation
from zope.proxy import ProxyBase, getProxiedObject, non_overridable
from zope.proxy.decorator import DecoratorSpecificationDescriptor
def inside(model1, model2):
"""Is ``model1`` 'inside' ``model2``? Return ``True`` if so, else
``False``.
``model1`` is 'inside' ``model2`` if ``model2`` is a `location
ancestor` of ``model1``. It is a location ancestor if its parent
(or one of its parent's parents, etc.) is an ancestor.
"""
while model1 is not None:
if model1 is model2:
return True
model1 = model1.__parent__
return False
def locate(model, parent, name=None):
"""
If ``model`` explicitly provides the
``repoze.bfg.interfaces.ILocation`` interface, locate ``model``
directly set ``model`` 's ``__parent__`` attribute to the
``parent`` object (also a model), and its ``__name__`` to the
supplied ``name`` argument.
If ``model`` does *not* explicitly provide the
``repoze.bfg.interfaces.ILocation`` interface, return a
``LocationProxy`` object representing ``model`` with its
``__parent__`` attribute assigned to ``parent`` and a ``__name__``
attribute assigned to ``__name__``. A ``LocationProxy`` object is
an unpickleable proxy that can 'stand in' for arbitrary object
instances.
"""
if ILocation.providedBy(model):
if parent is not model.__parent__ or name != model.__name__:
_locate(model, parent, name)
return model
return LocationProxy(model, parent, name)
def lineage(model):
"""
Return a generator representing the model lineage. The generator
first returns ``model`` unconditionally. Then, if ``model``
supplies a ``__parent__`` attribute, return the object represented
by ``model.__parent__``. If *that* object has a ``__parent__``
attribute, return that object's parent, and so on, until the
object being inspected either has no ``__parent__`` attribute or
which has a ``__parent__`` attribute of ``None``. For example, if
the object tree is::
thing1 = Thing()
thing2 = Thing()
thing2.__parent__ = thing1
Calling ``lineage(thing2)`` will return a generator. When we turn
it into a list, we will get::
list(lineage(thing2))
[ <Thing object at thing2>, <Thing object at thing1> ]
"""
while model is not None:
yield model
model = getattr(model, '__parent__', None)
def _locate(model, parent, name=None):
model.__parent__ = parent
model.__name__ = name
class ClassAndInstanceDescr(object):
def __init__(self, *args):
self.funcs = args
def __get__(self, inst, cls):
if inst is None:
return self.funcs[1](cls)
return self.funcs[0](inst)
class LocationProxy(ProxyBase):
"""Location-object proxy
This is a non-picklable proxy that can be put around objects that
don't implement `ILocation`.
"""
zope.interface.implements(ILocation)
__slots__ = '__parent__', '__name__'
__safe_for_unpickling__ = True
def __new__(self, ob, container=None, name=None):
return ProxyBase.__new__(self, ob)
def __init__(self, ob, container=None, name=None):
ProxyBase.__init__(self, ob)
self.__parent__ = container
self.__name__ = name
@non_overridable
def __reduce__(self, proto=None):
raise TypeError("Not picklable")
__doc__ = ClassAndInstanceDescr(
lambda inst: getProxiedObject(inst).__doc__,
lambda cls, __doc__ = __doc__: __doc__,
)
__reduce_ex__ = __reduce__
__providedBy__ = DecoratorSpecificationDescriptor()
|