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

zopefoundation / zope.component / 16248888290

25 Jun 2025 07:38AM UTC coverage: 99.774%. Remained the same
16248888290

push

github

web-flow
Merge pull request #77 from adrian-zon/patch-1

Fix typo

241 of 252 branches covered (95.63%)

Branch coverage included in aggregate %.

4615 of 4615 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.
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

28
    def removeSecurityProxy(x):
29
        return x
30

31

32
from zope.interface.interfaces import ComponentLookupError
1✔
33
from zope.interface.interfaces import IComponentLookup
1✔
34

35
from zope.component.globalregistry import getGlobalSiteManager
1✔
36

37

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

47

48
class read_property:
1✔
49
    """Descriptor for property-like computed attributes.
50

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

55
    def __init__(self, func):
1✔
56
        self.func = func
1✔
57

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

62
        return self.func(inst)
1✔
63

64

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

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

75

76
siteinfo = SiteInfo()
1✔
77

78

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

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

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

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

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

102

103
def getSite():
1✔
104
    return siteinfo.site
1✔
105

106

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

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

122

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

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

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

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

142

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

149

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

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

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

165

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

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

189

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