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

zopefoundation / zope.component / 4696882551

pending completion
4696882551

push

github

Michael Howitz
Preparing release 6.0

460 of 475 branches covered (96.84%)

Branch coverage included in aggregate %.

4646 of 4646 relevant lines covered (100.0%)

1.0 hits per line

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

100.0
/src/zope/component/hooks.py
1
##############################################################################
2
#
3
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Hooks for getting and setting a site in the thread global namespace.
1✔
15
"""
16
__docformat__ = 'restructuredtext'
1✔
17

18
import contextlib
1✔
19
import threading
1✔
20

21
from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX
1✔
22

23

24
try:
1✔
25
    from zope.security.proxy import removeSecurityProxy
1✔
26
except ZOPE_SECURITY_NOT_AVAILABLE_EX:  # pragma: no cover
27
    def removeSecurityProxy(x):
28
        return x
29

30
from zope.interface.interfaces import ComponentLookupError
1✔
31
from zope.interface.interfaces import IComponentLookup
1✔
32

33
from zope.component.globalregistry import getGlobalSiteManager
1✔
34

35

36
__all__ = [
1✔
37
    'setSite',
38
    'getSite',
39
    'site',
40
    'getSiteManager',
41
    'setHooks',
42
    'resetHooks',
43
]
44

45

46
class read_property:
1✔
47
    """Descriptor for property-like computed attributes.
48

49
    Unlike the standard 'property', this descriptor allows assigning a
50
    value to the instance, shadowing the property getter function.
51
    """
52

53
    def __init__(self, func):
1✔
54
        self.func = func
1✔
55

56
    def __get__(self, inst, cls):
1✔
57
        if inst is None:
1✔
58
            return self
1✔
59

60
        return self.func(inst)
1✔
61

62

63
class SiteInfo(threading.local):
1✔
64
    site = None
1✔
65
    sm = getGlobalSiteManager()
1✔
66

67
    @read_property
1✔
68
    def adapter_hook(self):
1✔
69
        adapter_hook = self.sm.adapters.adapter_hook
1✔
70
        self.adapter_hook = adapter_hook
1✔
71
        return adapter_hook
1✔
72

73

74
siteinfo = SiteInfo()
1✔
75

76

77
def setSite(site=None):
1✔
78
    if site is None:
1✔
79
        sm = getGlobalSiteManager()
1✔
80
    else:
81

82
        # We remove the security proxy because there's no way for
83
        # untrusted code to get at it without it being proxied again.
84

85
        # We should really look look at this again though, especially
86
        # once site managers do less.  There's probably no good reason why
87
        # they can't be proxied.  Well, except maybe for performance.
88

89
        site = removeSecurityProxy(site)
1✔
90
        # The getSiteManager method is defined by IPossibleSite.
91
        sm = site.getSiteManager()
1✔
92

93
    siteinfo.site = site
1✔
94
    siteinfo.sm = sm
1✔
95
    try:
1✔
96
        del siteinfo.adapter_hook
1✔
97
    except AttributeError:
1✔
98
        pass
1✔
99

100

101
def getSite():
1✔
102
    return siteinfo.site
1✔
103

104

105
@contextlib.contextmanager
1✔
106
def site(site):
1✔
107
    """
108
    site(site) -> None
109

110
    Context manager that sets *site* as the current site for the
111
    duration of the ``with`` body.
112
    """
113
    old_site = getSite()
1✔
114
    setSite(site)
1✔
115
    try:
1✔
116
        yield
1✔
117
    finally:
118
        setSite(old_site)
1✔
119

120

121
def getSiteManager(context=None):
1✔
122
    """A special hook for getting the site manager.
123

124
    Here we take the currently set site into account to find the appropriate
125
    site manager.
126
    """
127
    if context is None:
1✔
128
        return siteinfo.sm
1✔
129

130
    # We remove the security proxy because there's no way for
131
    # untrusted code to get at it without it being proxied again.
132

133
    # We should really look look at this again though, especially
134
    # once site managers do less.  There's probably no good reason why
135
    # they can't be proxied.  Well, except maybe for performance.
136
    sm = IComponentLookup(
1✔
137
        context, getGlobalSiteManager())
138
    sm = removeSecurityProxy(sm)
1✔
139
    return sm
1✔
140

141

142
def adapter_hook(interface, object, name='', default=None):
1✔
143
    try:
1✔
144
        return siteinfo.adapter_hook(interface, object, name, default)
1✔
145
    except ComponentLookupError:
1✔
146
        return default
1✔
147

148

149
def setHooks():
1✔
150
    """
151
    Make `zope.component.getSiteManager` and interface adaptation
152
    respect the current site.
153

154
    Most applications will want to be sure te call this early in their
155
    startup sequence. Test code that uses these APIs should also arrange to
156
    call this.
157

158
    .. seealso:: :mod:`zope.component.testlayer`
159
    """
160
    from zope.component import _api
1✔
161
    _api.adapter_hook.sethook(adapter_hook)
1✔
162
    _api.getSiteManager.sethook(getSiteManager)
1✔
163

164

165
def resetHooks():
1✔
166
    """
167
    Reset `zope.component.getSiteManager` and interface adaptation to
168
    their original implementations that are unaware of the current
169
    site.
170

171
    Use caution when calling this; most code will not need to call
172
    this. If code using the global API executes following this, it
173
    will most likely use the base global component registry instead of
174
    a site-specific registry it was expected. This can lead to
175
    failures in adaptation and utility lookup.
176
    """
177
    # Reset hookable functions to original implementation.
178
    from zope.component import _api
1✔
179
    _api.adapter_hook.reset()
1✔
180
    _api.getSiteManager.reset()
1✔
181
    # be sure the old adapter hook isn't cached, since
182
    # it is derived from the SiteManager
183
    try:
1✔
184
        del siteinfo.adapter_hook
1✔
185
    except AttributeError:
1✔
186
        pass
1✔
187

188

189
# Clear the site thread global
190
clearSite = setSite
1✔
191
try:
1✔
192
    from zope.testing.cleanup import addCleanUp
1✔
193
except ImportError:  # pragma: no cover
194
    pass
195
else:
196
    addCleanUp(resetHooks)
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