/usr/share/pyshared/zope/app/appsetup/product.txt is in python-zope.app.appsetup 3.16.0-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | Product-specific configuration
==============================
The ``product`` module of this package provides a very simple way to deal with
what has traditionally been called "product configuration", where "product"
refers to the classic Zope 2 notion of a product.
The configuration schema for the application server allows named
<product-config> sections to be added to the configuration file, and product
code can use the API provided by the module to retrieve configuration sections
for given names.
There are two public functions in the module that should be used in normal
operations, and additional functions and a class that can be used to help with
testing:
>>> from zope.app.appsetup import product
Let's look at the helper class first, since we'll use it in describing the
public (application) interface. We'll follow that with the functions for
normal operation, then the remaining test-support functions.
Faux configuration object
-------------------------
The ``FauxConfiguration`` class constructs objects that behave like the
ZConfig section objects to the extent needed for the product configuration
API. These will be used here, and may also be used to create configurations
for testing components that consume such configuration.
The constructor requires two arguments: the name of the section, and a mapping
of keys to values that the section should provide. Let's create a simple
example:
>>> one = product.FauxConfiguration("one", {})
>>> one.getSectionName()
'one'
>>> one.mapping
{}
Providing a non-empty set of key/value pairs trivially behaves as expected:
>>> two = product.FauxConfiguration("two", {"abc": "def"})
>>> two.getSectionName()
'two'
>>> two.mapping
{'abc': 'def'}
Application API
---------------
There are two functions in the application interface for this module. One is
used by the configuration provider, and the other is used by the consumer.
The provider's API takes a sequence of configuration objects that conform to
the behaviors exhibited by the default ZConfig section objects. Since the
``FauxConfiguration`` class provides these behaviors, we can easily see how
this can be used:
>>> product.setProductConfigurations([one, two])
Now that we've established some configuration, we want to be able to use it.
We do this using the ``getProductConfiguration()`` function. This function
takes a name and returns a matching configuration section if there is one, of
None if not:
>>> product.getProductConfiguration("one")
{}
>>> product.getProductConfiguration("not-there") is None
True
Note that for a section that exists, only the internal mapping is provided,
not the containing section object. This is a historical wart; we'll just need
to live with it until new APIs are introduced.
Setting the configuration a second time will overwrite the prior
configuration; sections previously available will no longer be:
>>> product.setProductConfigurations([two])
>>> product.getProductConfiguration("one") is None
True
The new sections are available, as expected:
>>> product.getProductConfiguration("two")
{'abc': 'def'}
Test support functions
----------------------
Additional functions are provided that make it easier to manage configuration
state in testing.
The first can be used to provide configuration for a single name. The
function takes a name and either a configuration mapping or ``None`` as
arguments. If ``None`` is provided as the second argument, any configuration
settings for the name are removed, if present. If the second argument is not
``None``, it will be used as the return value for ``getProductConfiguration``
for the given name.
>>> product.setProductConfiguration("first", None)
>>> print product.getProductConfiguration("first")
None
>>> product.setProductConfiguration("first", {"key": "value1"})
>>> product.getProductConfiguration("first")
{'key': 'value1'}
>>> product.setProductConfiguration("first", {"key": "value2"})
>>> product.getProductConfiguration("first")
{'key': 'value2'}
>>> product.setProductConfiguration("first", {"alt": "another"})
>>> product.getProductConfiguration("first")
{'alt': 'another'}
>>> product.setProductConfiguration("second", {"you": "there"})
>>> product.getProductConfiguration("first")
{'alt': 'another'}
>>> product.getProductConfiguration("second")
{'you': 'there'}
>>> product.setProductConfiguration("first", None)
>>> print product.getProductConfiguration("first")
None
The other two functions work in concert, saving and restoring the entirety of
the configuration state.
Our current configuration includes data for the "second" key, and none for the
"first" key:
>>> print product.getProductConfiguration("first")
None
>>> print product.getProductConfiguration("second")
{'you': 'there'}
Let's save this state:
>>> state = product.saveConfiguration()
Now let's replace the kitchen sink:
>>> product.setProductConfigurations([
... product.FauxConfiguration("x", {"a": "b"}),
... product.FauxConfiguration("y", {"c": "d"}),
... ])
>>> print product.getProductConfiguration("first")
None
>>> print product.getProductConfiguration("second")
None
>>> product.getProductConfiguration("x")
{'a': 'b'}
>>> product.getProductConfiguration("y")
{'c': 'd'}
The saved configuration state can be restored:
>>> product.restoreConfiguration(state)
>>> print product.getProductConfiguration("x")
None
>>> print product.getProductConfiguration("y")
None
>>> print product.getProductConfiguration("first")
None
>>> print product.getProductConfiguration("second")
{'you': 'there'}
There's an additional function that can be used to load product configuration
from a file object; only product configuration components are accepted. The
function returns a mapping of names to configuration objects suitable for
passing to ``setProductConfiguration``. Using this with
``setProductConfigurations`` would require constructing ``FauxConfiguration``
objects.
Let's create some sample configuration text:
>>> product_config = '''
... <product-config product1>
... key1 product1-value1
... key2 product1-value2
... </product-config>
...
... <product-config product2>
... key1 product2-value1
... key3 product2-value2
... </product-config>
... '''
We can now load the configuration using the ``loadConfiguration`` function:
>>> import StringIO
>>> import pprint
>>> sio = StringIO.StringIO(product_config)
>>> config = product.loadConfiguration(sio)
>>> pprint.pprint(config, width=1)
{'product1': {'key1': 'product1-value1',
'key2': 'product1-value2'},
'product2': {'key1': 'product2-value1',
'key3': 'product2-value2'}}
Extensions that provide product configurations can be used as well:
>>> product_config = '''
... %import zope.app.appsetup.testproduct
...
... <testproduct foobar>
... </testproduct>
...
... <testproduct barfoo>
... key1 value1
... key2 value2
... </testproduct>
... '''
>>> sio = StringIO.StringIO(product_config)
>>> config = product.loadConfiguration(sio)
>>> pprint.pprint(config, width=1)
{'barfoo': {'key1': 'value1',
'key2': 'value2',
'product-name': 'barfoo'},
'foobar': {'product-name': 'foobar'}}
|