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

zopefoundation / zope.app.appsetup / 15237677029

25 Apr 2025 06:42AM UTC coverage: 87.007% (-0.3%) from 87.302%
15237677029

push

github

web-flow
Update Python version support. (#12)

* Drop support for Python 3.7, 3.8.

* Add support for Python 3.12, 3.13.

55 of 78 branches covered (70.51%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 2 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

474 of 530 relevant lines covered (89.43%)

0.89 hits per line

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

71.0
/src/zope/app/appsetup/appsetup.py
1
##############################################################################
2
#
3
# Copyright (c) 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
"""Code to initialize the application server."""
15
import ZODB.ActivityMonitor
1✔
16
import ZODB.interfaces
1✔
17
import zope.component
1✔
18
import zope.component.hooks
1✔
19
import zope.interface
1✔
20
import zope.processlifetime
1✔
21
from zope.security.interfaces import IParticipation
1✔
22
from zope.security.management import system_user
1✔
23

24

25
@zope.interface.implementer(IParticipation)
1✔
26
class SystemConfigurationParticipation:
1✔
27

28
    principal = system_user
1✔
29
    interaction = None
1✔
30

31

32
_configured = False
1✔
33

34

35
def config(file, features=(), execute=True):
1✔
36
    r"""Execute the ZCML configuration file.
37

38
    This procedure defines the global site setup. Optionally you can also
39
    provide a list of features that are inserted in the configuration context
40
    before the execution is started.
41

42
    Let's create a trivial sample ZCML file.
43

44
      >>> import tempfile
45
      >>> fn = tempfile.mktemp('.zcml')
46
      >>> zcml = open(fn, 'w')
47
      >>> written = zcml.write('''
48
      ... <configure xmlns:meta="http://namespaces.zope.org/meta"
49
      ...            xmlns:zcml="http://namespaces.zope.org/zcml">
50
      ...   <meta:provides feature="myFeature" />
51
      ...   <configure zcml:condition="have myFeature2">
52
      ...     <meta:provides feature="myFeature4" />
53
      ...   </configure>
54
      ... </configure>
55
      ... ''')
56
      >>> zcml.close()
57

58
    We can now pass the file into the `config()` function:
59

60
      # End an old interaction first
61
      >>> from zope.security.management import endInteraction
62
      >>> endInteraction()
63

64
      >>> context = config(fn, features=('myFeature2', 'myFeature3'))
65
      >>> context.hasFeature('myFeature')
66
      True
67
      >>> context.hasFeature('myFeature2')
68
      True
69
      >>> context.hasFeature('myFeature3')
70
      True
71
      >>> context.hasFeature('myFeature4')
72
      True
73

74
    Further, we should have access to the configuration file name and context
75
    now:
76

77
      >>> getConfigSource() is fn
78
      True
79
      >>> getConfigContext() is context
80
      True
81

82
    Let's now clean up by removing the temporary file:
83

84
      >>> import os
85
      >>> os.remove(fn)
86

87
    """
88
    global _configured
89
    global __config_source
90
    __config_source = file
1✔
91

92
    if _configured:
1✔
93
        return
1✔
94

95
    from zope.configuration import config
1✔
96
    from zope.configuration import xmlconfig
1✔
97
    # Set user to system_user, so we can do anything we want
98
    from zope.security.management import newInteraction
1✔
99
    newInteraction(SystemConfigurationParticipation())
1✔
100

101
    # Hook up custom component architecture calls
102
    zope.component.hooks.setHooks()
1✔
103

104
    # Load server-independent site config
105
    context = config.ConfigurationMachine()
1✔
106
    xmlconfig.registerCommonDirectives(context)
1✔
107
    for feature in features:
1✔
108
        context.provideFeature(feature)
1✔
109
    context = xmlconfig.file(file, context=context, execute=execute)
1✔
110

111
    # Reset user
112
    from zope.security.management import endInteraction
1✔
113
    endInteraction()
1✔
114

115
    _configured = execute
1✔
116

117
    global __config_context
118
    __config_context = context
1✔
119

120
    return context
1✔
121

122

123
def database(db):
1✔
124
    """Load ZODB database from Python module or FileStorage file"""
125
    if type(db) is str:
×
126
        # Database name
127
        if db.endswith('.py'):
×
128
            # Python source, exec it
129
            globals = {}
×
130
            exec(compile(open(db).read(), db, 'exec'), globals)
×
131
            if 'DB' in globals:
×
132
                db = globals['DB']
×
133
            else:
134
                storage = globals['Storage']
×
135
                from ZODB.DB import DB
×
136
                db = DB(storage, cache_size=4000)
×
137
        elif db.endswith(".fs"):
×
138
            from ZODB.DB import DB
×
139
            from ZODB.FileStorage import FileStorage
×
140
            storage = FileStorage(db)
×
141
            db = DB(storage, cache_size=4000)
×
142

143
    # The following will fail unless the application has been configured.
144
    from zope.event import notify
×
145
    notify(zope.processlifetime.DatabaseOpened(db))
×
146

147
    return db
×
148

149

150
def multi_database(database_factories):
1✔
151
    """Set up a multi-database from an iterable of database factories
152

153
    Return a sequence of databases, and a mapping of from database name to
154
    database.
155

156
    >>> class DB:
157
    ...     def __init__(self, number):
158
    ...         self.number = number
159
    ...     def __repr__(self):
160
    ...         return "DB(%s)" % self.number
161
    ...     def getActivityMonitor(self):
162
    ...         return self._activity_monitor
163
    ...     def setActivityMonitor(self, am):
164
    ...         self._activity_monitor = am
165

166
    >>> class Factory:
167
    ...     def __init__(self, name, number):
168
    ...         self.name = name
169
    ...         self.number = number
170
    ...     def open(self):
171
    ...         return DB(self.number)
172

173
    >>> s, m = multi_database(
174
    ...           [Factory(None, 3), Factory('y', 2), Factory('x', 1)])
175

176
    >>> list(s)
177
    [DB(3), DB(2), DB(1)]
178

179
    >>> [d.database_name for d in s]
180
    ['', 'y', 'x']
181

182
    >>> [d.databases is m for d in s]
183
    [True, True, True]
184

185
    >>> items = m.items()
186
    >>> sorted(list(items))
187
    [('', DB(3)), ('x', DB(1)), ('y', DB(2))]
188

189
    Each of the databases is registered as an IDatabase utility:
190

191
    >>> from zope import component
192
    >>> [(component.getUtility(ZODB.interfaces.IDatabase, name) is m[name])
193
    ...  for name in m]
194
    [True, True, True]
195

196
    And has an activity monitor:
197

198
    >>> [isinstance(db.getActivityMonitor(),
199
    ...             ZODB.ActivityMonitor.ActivityMonitor)
200
    ...  for db in m.values()]
201
    [True, True, True]
202

203
    """
204
    databases = {}
1✔
205
    result = []
1✔
206
    for factory in database_factories:
1✔
207
        name = factory.name or ''
1✔
208
        if name in databases:
1!
209
            raise ValueError("Duplicate database name: %r" % name)
×
210
        db = factory.open()
1✔
211
        db.databases = databases
1✔
212
        db.database_name = name
1✔
213
        databases[name] = db
1✔
214
        # Grrr bug in ZODB. Database doesn't declare that it implements
215
        # IDatabase.
216
        if not ZODB.interfaces.IDatabase.providedBy(db):
1✔
217
            zope.interface.directlyProvides(db, ZODB.interfaces.IDatabase)
1✔
218
        zope.component.provideUtility(db, ZODB.interfaces.IDatabase, name)
1✔
219
        db.setActivityMonitor(ZODB.ActivityMonitor.ActivityMonitor())
1✔
220
        result.append(db)
1✔
221

222
    return result, databases
1✔
223

224

225
__config_context = None
1✔
226

227

228
def getConfigContext():
1✔
229
    return __config_context
1✔
230

231

232
__config_source = None
1✔
233

234

235
def getConfigSource():
1✔
236
    return __config_source
1✔
237

238

239
def reset():
1✔
240
    global _configured
241
    _configured = False
1✔
242

243
    global __config_source
244
    __config_source = None
1✔
245

246
    global __config_context
247
    __config_context = None
1✔
248

249

250
try:
1✔
251
    import zope.testing.cleanup
1✔
NEW
252
except ModuleNotFoundError:
×
UNCOV
253
    pass
×
254
else:
255
    zope.testing.cleanup.addCleanUp(reset)
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