• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.46
/src/OFS/Traversable.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
"""This module implements a mix-in for traversable objects.
1✔
14
"""
15

16
from urllib.parse import quote
1✔
17

18
from AccessControl.class_init import InitializeClass
1✔
19
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
20
from AccessControl.SecurityManagement import getSecurityManager
1✔
21
from AccessControl.unauthorized import Unauthorized
1✔
22
from AccessControl.ZopeGuards import guarded_getattr
1✔
23
from Acquisition import Acquired
1✔
24
from Acquisition import aq_acquire
1✔
25
from Acquisition import aq_base
1✔
26
from Acquisition import aq_inner
1✔
27
from Acquisition import aq_parent
1✔
28
from Acquisition.interfaces import IAcquirer
1✔
29
from OFS.interfaces import IApplication
1✔
30
from OFS.interfaces import ITraversable
1✔
31
from zExceptions import NotFound
1✔
32
from ZODB.POSException import ConflictError
1✔
33
from zope.component import queryMultiAdapter
1✔
34
from zope.interface import Interface
1✔
35
from zope.interface import implementer
1✔
36
from zope.location.interfaces import LocationError
1✔
37
from zope.traversing.namespace import namespaceLookup
1✔
38
from zope.traversing.namespace import nsParse
1✔
39
from ZPublisher.interfaces import UseTraversalDefault
1✔
40

41

42
_marker = object()
1✔
43

44

45
@implementer(ITraversable)
1✔
46
class Traversable:
1✔
47

48
    security = ClassSecurityInfo()
1✔
49

50
    @security.public
1✔
51
    def absolute_url(self, relative=0):
1✔
52
        """Return the absolute URL of the object.
53

54
        This a canonical URL based on the object's physical
55
        containment path.  It is affected by the virtual host
56
        configuration, if any, and can be used by external
57
        agents, such as a browser, to address the object.
58

59
        If the relative argument is provided, with a true value, then
60
        the value of virtual_url_path() is returned.
61

62
        Some Products incorrectly use '/'+absolute_url(1) as an
63
        absolute-path reference.  This breaks in certain virtual
64
        hosting situations, and should be changed to use
65
        absolute_url_path() instead.
66
        """
67
        if relative:
1✔
68
            return self.virtual_url_path()
1✔
69

70
        spp = self.getPhysicalPath()
1✔
71

72
        try:
1✔
73
            toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
1✔
74
        except AttributeError:
1✔
75
            return path2url(spp[1:])
1✔
76
        return toUrl(spp)
1✔
77

78
    @security.public
1✔
79
    def absolute_url_path(self):
1✔
80
        """Return the path portion of the absolute URL of the object.
81

82
        This includes the leading slash, and can be used as an
83
        'absolute-path reference' as defined in RFC 2396.
84
        """
85
        spp = self.getPhysicalPath()
1✔
86
        try:
1✔
87
            toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
1✔
88
        except AttributeError:
×
89
            return path2url(spp) or '/'
×
90
        return toUrl(spp, relative=1) or '/'
1✔
91

92
    @security.public
1✔
93
    def virtual_url_path(self):
1✔
94
        """Return a URL for the object, relative to the site root.
95

96
        If a virtual host is configured, the URL is a path relative to
97
        the virtual host's root object.  Otherwise, it is the physical
98
        path.  In either case, the URL does not begin with a slash.
99
        """
100
        spp = self.getPhysicalPath()
1✔
101
        try:
1✔
102
            toVirt = aq_acquire(self, 'REQUEST').physicalPathToVirtualPath
1✔
103
        except AttributeError:
×
104
            return path2url(spp[1:])
×
105
        return path2url(toVirt(spp))
1✔
106

107
    # decorators did not work on variables
108
    security.declarePrivate('getPhysicalRoot')  # NOQA: D001
1✔
109
    getPhysicalRoot = Acquired
1✔
110

111
    @security.public
1✔
112
    def getPhysicalPath(self):
1✔
113
        # Get the physical path of the object.
114
        #
115
        # Returns a path (an immutable sequence of strings) that can be used to
116
        # access this object again later, for example in a copy/paste
117
        # operation.  getPhysicalRoot() and getPhysicalPath() are designed to
118
        # operate together.
119

120
        # This implementation is optimized to avoid excessive amounts of
121
        # function calls while walking up from an object on a deep level.
122
        try:
1✔
123
            id = self.id or self.getId()
1✔
124
        except AttributeError:
×
125
            id = self.getId()
×
126

127
        path = (id, )
1✔
128
        p = aq_parent(aq_inner(self))
1✔
129
        if p is None:
1✔
130
            return path
1✔
131

132
        func = self.getPhysicalPath.__func__
1✔
133
        while p is not None:
1✔
134
            if func is p.getPhysicalPath.__func__:
1✔
135
                try:
1✔
136
                    pid = p.id or p.getId()
1✔
137
                except AttributeError:
×
138
                    pid = p.getId()
×
139

140
                path = (pid, ) + path
1✔
141
                p = aq_parent(aq_inner(p))
1✔
142
            else:
143
                if IApplication.providedBy(p):
1!
144
                    path = ('', ) + path
1✔
145
                else:
146
                    path = p.getPhysicalPath() + path
×
147
                break
×
148

149
        return path
1✔
150

151
    @security.private
1✔
152
    def unrestrictedTraverse(self, path, default=_marker, restricted=False):
1✔
153
        """Lookup an object by path.
154

155
        path -- The path to the object. May be a sequence of strings or a slash
156
        separated string. If the path begins with an empty path element
157
        (i.e., an empty string or a slash) then the lookup is performed
158
        from the application root. Otherwise, the lookup is relative to
159
        self. Two dots (..) as a path element indicates an upward traversal
160
        to the acquisition parent.
161

162
        default -- If provided, this is the value returned if the path cannot
163
        be traversed for any reason (i.e., no object exists at that path or
164
        the object is inaccessible).
165

166
        restricted -- If false (default) then no security checking is
167
        performed. If true, then all of the objects along the path are
168
        validated with the security machinery. Usually invoked using
169
        restrictedTraverse().
170
        """
171
        if not path:
1✔
172
            return self
1✔
173

174
        if isinstance(path, str):
1✔
175
            path = path.split('/')
1✔
176
        else:
177
            path = list(path)
1✔
178
            for part in path:
1✔
179
                if not isinstance(part, str):
1✔
180
                    raise TypeError(
1✔
181
                        "path should be a string or an iterable of strings"
182
                    )
183

184
        REQUEST = {'TraversalRequestNameStack': path}
1✔
185
        path.reverse()
1✔
186
        path_pop = path.pop
1✔
187

188
        if len(path) > 1 and not path[0]:
1✔
189
            # Remove trailing slash
190
            path_pop(0)
1✔
191

192
        if restricted:
1✔
193
            validate = getSecurityManager().validate
1✔
194

195
        if not path[-1]:
1✔
196
            # If the path starts with an empty string, go to the root first.
197
            path_pop()
1✔
198
            obj = self.getPhysicalRoot()
1✔
199
            if restricted:
1✔
200
                validate(None, None, None, obj)  # may raise Unauthorized
1✔
201
        else:
202
            obj = self
1✔
203

204
        # import time ordering problem
205
        from webdav.NullResource import NullResource
1✔
206
        resource = _marker
1✔
207

208
        try:
1✔
209
            while path:
1✔
210
                name = path_pop()
1✔
211
                __traceback_info__ = path, name
1✔
212

213
                if name[0] == '_':
1!
214
                    # Never allowed in a URL.
215
                    raise NotFound(name)
×
216

217
                if name == '..':
1✔
218
                    next = aq_parent(obj)
1✔
219
                    if next is not None:
1!
220
                        if restricted and not validate(obj, obj, name, next):
1!
221
                            raise Unauthorized(name)
×
222
                        obj = next
1✔
223
                        continue
1✔
224

225
                bobo_traverse = getattr(obj, '__bobo_traverse__', None)
1✔
226
                try:
1✔
227
                    if (
1✔
228
                        name
229
                        and name[:1] in '@+'
230
                        and name != '+'
231
                        and nsParse(name)[1]
232
                    ):
233
                        # Process URI segment parameters.
234
                        ns, nm = nsParse(name)
1✔
235
                        try:
1✔
236
                            next = namespaceLookup(
1✔
237
                                ns, nm, obj, aq_acquire(self, 'REQUEST'))
238
                            if IAcquirer.providedBy(next):
1!
239
                                next = next.__of__(obj)
×
240
                            if restricted and not validate(
1!
241
                                    obj, obj, name, next):
242
                                raise Unauthorized(name)
×
243
                        except LocationError:
1✔
244
                            raise AttributeError(name)
1✔
245

246
                    else:
247
                        next = UseTraversalDefault  # indicator
1✔
248
                        try:
1✔
249
                            if bobo_traverse is not None:
1✔
250
                                next = bobo_traverse(REQUEST, name)
1✔
251
                                if restricted:
1✔
252
                                    if aq_base(next) is not next:
1✔
253
                                        # The object is wrapped, so the
254
                                        # acquisition context is the container.
255
                                        container = aq_parent(aq_inner(next))
1✔
256
                                    elif getattr(next, '__self__',
1✔
257
                                                 None) is not None:
258
                                        # Bound method, the bound instance
259
                                        # is the container
260
                                        container = next.__self__
1✔
261
                                    elif getattr(
1✔
262
                                            aq_base(obj),
263
                                            name, _marker) is next:
264
                                        # Unwrapped direct attribute of the
265
                                        # object so object is the container
266
                                        container = obj
1✔
267
                                    else:
268
                                        # Can't determine container
269
                                        container = None
1✔
270
                                    # If next is a simple unwrapped property,
271
                                    # its parentage is indeterminate, but it
272
                                    # may have been acquired safely. In this
273
                                    # case validate will raise an error, and
274
                                    # we can explicitly check that our value
275
                                    # was acquired safely.
276
                                    try:
1✔
277
                                        ok = validate(
1✔
278
                                            obj, container, name, next)
279
                                    except Unauthorized:
1✔
280
                                        ok = False
1✔
281
                                    if not ok:
1✔
282
                                        if (
1✔
283
                                            container is not None
284
                                            or guarded_getattr(obj, name, _marker) is not next  # NOQA: E501
285
                                        ):
286
                                            raise Unauthorized(name)
1✔
287
                        except UseTraversalDefault:
1✔
288
                            # behave as if there had been no
289
                            # '__bobo_traverse__'
290
                            bobo_traverse = None
1✔
291
                        if next is UseTraversalDefault:
1✔
292
                            if getattr(
1✔
293
                                    aq_base(obj),
294
                                    name, _marker) is not _marker:
295
                                if restricted:
1✔
296
                                    next = guarded_getattr(obj, name)
1✔
297
                                else:
298
                                    next = getattr(obj, name)
1✔
299
                            else:
300
                                try:
1✔
301
                                    next = obj[name]
1✔
302
                                    # The item lookup may return a
303
                                    # NullResource, if this is the case we
304
                                    # save it and return it if all other
305
                                    # lookups fail.
306
                                    if isinstance(next, NullResource):
1!
307
                                        resource = next
×
308
                                        raise KeyError(name)
×
309
                                except (AttributeError, TypeError):
1✔
310
                                    # Raise NotFound for easier debugging
311
                                    # instead of AttributeError: __getitem__
312
                                    # or TypeError: not subscriptable
313
                                    raise NotFound(name)
1✔
314
                                if restricted and not validate(
1!
315
                                        obj, obj, None, next):
316
                                    raise Unauthorized(name)
×
317

318
                except (AttributeError, NotFound, KeyError) as e:
1✔
319
                    # Try to look for a view
320
                    next = queryMultiAdapter(
1✔
321
                        (obj, aq_acquire(self, 'REQUEST')),
322
                        Interface, name)
323

324
                    if next is not None:
1✔
325
                        if IAcquirer.providedBy(next):
1✔
326
                            next = next.__of__(obj)
1✔
327
                        if restricted and not validate(obj, obj, name, next):
1!
328
                            raise Unauthorized(name)
×
329
                    elif bobo_traverse is not None:
1✔
330
                        # Attribute lookup should not be done after
331
                        # __bobo_traverse__:
332
                        raise e
1✔
333
                    else:
334
                        # No view, try acquired attributes
335
                        try:
1✔
336
                            if restricted:
1✔
337
                                next = guarded_getattr(obj, name, _marker)
1✔
338
                            else:
339
                                next = getattr(obj, name, _marker)
1✔
340
                        except AttributeError:
1✔
341
                            raise e
×
342
                        if next is _marker:
1✔
343
                            # If we have a NullResource from earlier use it.
344
                            next = resource
1✔
345
                            if next is _marker:
1!
346
                                # Nothing found re-raise error
347
                                raise e
1✔
348

349
                obj = next
1✔
350

351
            return obj
1✔
352

353
        except ConflictError:
1!
354
            raise
×
355
        except Exception:
1✔
356
            if default is not _marker:
1✔
357
                return default
1✔
358
            else:
359
                raise
1✔
360

361
    @security.public
1✔
362
    def restrictedTraverse(self, path, default=_marker):
1✔
363
        # Trusted code traversal code, always enforces securitys
364
        return self.unrestrictedTraverse(path, default, restricted=True)
1✔
365

366

367
InitializeClass(Traversable)
1✔
368

369

370
def path2url(path):
1✔
371
    return '/'.join(map(quote, path))
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc