This file is indexed.

/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'}}