~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/lazr/interfaces/config.py

  • Committer: Curtis Hovey
  • Date: 2011-12-29 05:29:36 UTC
  • mto: This revision was merged to the branch mainline in revision 14606.
  • Revision ID: curtis.hovey@canonical.com-20111229052936-c261pibg1p6ze6m4
Moved canonical.config to lp.services.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
# pylint: disable-msg=E0211,E0213,W0231
 
5
"""Interfaces for process configuration.."""
 
6
 
 
7
__metaclass__ = type
 
8
 
 
9
__all__ = [
 
10
    'ConfigErrors',
 
11
    'ConfigSchemaError',
 
12
    'IConfigData',
 
13
    'NoConfigError',
 
14
    'ICategory',
 
15
    'IConfigLoader',
 
16
    'IConfigSchema',
 
17
    'InvalidSectionNameError',
 
18
    'ISection',
 
19
    'ISectionSchema',
 
20
    'IStackableConfig',
 
21
    'NoCategoryError',
 
22
    'RedefinedKeyError',
 
23
    'RedefinedSectionError',
 
24
    'UnknownKeyError',
 
25
    'UnknownSectionError']
 
26
 
 
27
from zope.interface import Interface, Attribute
 
28
 
 
29
 
 
30
class ConfigSchemaError(Exception):
 
31
    """A base class of all `IConfigSchema` errors."""
 
32
 
 
33
 
 
34
class RedefinedKeyError(ConfigSchemaError):
 
35
    """A key in a section cannot be redefined."""
 
36
 
 
37
 
 
38
class RedefinedSectionError(ConfigSchemaError):
 
39
    """A section in a config file cannot be redefined."""
 
40
 
 
41
 
 
42
class InvalidSectionNameError(ConfigSchemaError):
 
43
    """The section name contains more than one category."""
 
44
 
 
45
 
 
46
class NoCategoryError(LookupError):
 
47
    """No `ISectionSchema`s belong to the category name."""
 
48
 
 
49
 
 
50
class UnknownSectionError(ConfigSchemaError):
 
51
    """The config has a section that is not in the schema."""
 
52
 
 
53
 
 
54
class UnknownKeyError(ConfigSchemaError):
 
55
    """The section has a key that is not in the schema."""
 
56
 
 
57
class NoConfigError(ConfigSchemaError):
 
58
    """No config has the name."""
 
59
 
 
60
class ConfigErrors(ConfigSchemaError):
 
61
    """The errors in a Config.
 
62
 
 
63
    The list of errors can be accessed via the errors attribute.
 
64
    """
 
65
 
 
66
    def __init__(self, message, errors=None):
 
67
        """Initialize the error with a message and errors.
 
68
 
 
69
        :param message: a message string
 
70
        :param errors: a list of errors in the config, or None
 
71
        """
 
72
        self.message = message
 
73
        self.errors = errors
 
74
 
 
75
    def __str__(self):
 
76
        return '%s: %s' % (self.__class__.__name__, self.message)
 
77
 
 
78
 
 
79
class ISectionSchema(Interface):
 
80
    """Defines the valid keys and default values for a configuration group."""
 
81
    name = Attribute("The section name.")
 
82
    optional = Attribute("Is the section optional in the config?")
 
83
    category_and_section_names = Attribute(
 
84
        "A 2-Tuple of the category and specific name parts.")
 
85
 
 
86
    def __iter__():
 
87
        """Iterate over the keys."""
 
88
 
 
89
    def __contains__(name):
 
90
        """Return True or False if name is a key."""
 
91
 
 
92
    def __getitem__(key):
 
93
        """Return the default value of the key.
 
94
 
 
95
        :raise `KeyError`: if the key does not exist.
 
96
        """
 
97
 
 
98
 
 
99
class ISection(ISectionSchema):
 
100
    """Defines the values for a configuration group."""
 
101
    schema = Attribute("The ISectionSchema that defines this ISection.")
 
102
 
 
103
    def __getattr__(name):
 
104
        """Return the named key.
 
105
 
 
106
        :name: a key name.
 
107
        :return: the value of the matching key.
 
108
        :raise: AttributeError if there is no key with the name.
 
109
        """
 
110
 
 
111
class IConfigLoader(Interface):
 
112
    """A configuration file loader."""
 
113
 
 
114
    def load(filename):
 
115
        """Load a configuration from the file at filename."""
 
116
 
 
117
    def loadFile(source_file, filename=None):
 
118
        """Load a configuration from the open source_file.
 
119
 
 
120
        :param source_file: A file-like object that supports read() and
 
121
            readline()
 
122
        :param filename: The name of the configuration. If filename is None,
 
123
            The name will be taken from source_file.name.
 
124
        """
 
125
 
 
126
 
 
127
class IConfigSchema(Interface):
 
128
    """A process configuration schema.
 
129
 
 
130
    The config file contains sections enclosed in square brackets ([]).
 
131
    The section name may be divided into major and minor categories using a
 
132
    dot (.). Beneath each section is a list of key-value pairs, separated
 
133
    by a colon (:). Multiple sections with the same major category may have
 
134
    their keys defined in another section that appends the '.template'
 
135
    suffix to the category name. A section with '.optional' suffix is not
 
136
    required. Lines that start with a hash (#) are comments.
 
137
    """
 
138
    name = Attribute('The basename of the config filename.')
 
139
    filename = Attribute('The path to config file')
 
140
    category_names = Attribute('The list of section category names.')
 
141
 
 
142
    def __iter__():
 
143
        """Iterate over the `ISectionSchema`s."""
 
144
 
 
145
    def __contains__(name):
 
146
        """Return True or False if the name matches a `ISectionSchema`."""
 
147
 
 
148
    def __getitem__(name):
 
149
        """Return the `ISectionSchema` with the matching name.
 
150
 
 
151
        :raise `NoSectionError`: if the no ISectionSchema has the name.
 
152
        """
 
153
 
 
154
    def getByCategory(name):
 
155
        """Return a list of ISectionSchemas that belong to the category name.
 
156
 
 
157
        `ISectionSchema` names may be made from a category name and a group
 
158
        name, separated by a dot (.). The category is synonymous with a
 
159
        arbitrary resource such as a database or a vhost. Thus database.bugs
 
160
        and database.answers are two sections that both use the database
 
161
        resource.
 
162
 
 
163
        :raise `CategoryNotFound`: if no sections have a name that starts
 
164
            with the category name.
 
165
        """
 
166
 
 
167
 
 
168
class IConfigData(IConfigSchema):
 
169
    """A process configuration.
 
170
 
 
171
    See `IConfigSchema` for more information about the config file format.
 
172
    """
 
173
 
 
174
 
 
175
class IStackableConfig(IConfigSchema):
 
176
    """A configuration that is built from configs that extend each other.
 
177
 
 
178
    A config may extend another config so that a configuration for a
 
179
    process need only define the localized sections and keys. The
 
180
    configuration is constructed from a stack of data that defines,
 
181
    and redefines, the sections and keys in the configuration. Each config
 
182
    overlays its data to define the final configuration.
 
183
 
 
184
    A config file declares that is extends another using the 'extends' key
 
185
    in the 'meta' section of the config data file:
 
186
        [meta]
 
187
        extends: common.conf
 
188
 
 
189
    The push() and pop() methods can be used to test processes where the
 
190
    test environment must be configured differently.
 
191
    """
 
192
    schema = Attribute("The schema that defines the config.")
 
193
    data = Attribute("The current ConfigData. use by the config.")
 
194
    extends = Attribute("The ConfigData that this config extends.")
 
195
    overlays = Attribute("The stack of ConfigData that define this config.")
 
196
 
 
197
 
 
198
    def __getattr__(name):
 
199
        """Return the named section.
 
200
 
 
201
        :name: a section or category name.
 
202
        :return: the matching `ISection` or `ICategory`.
 
203
        :raise: AttributeError if there is no section or category with the
 
204
            name.
 
205
        """
 
206
 
 
207
    def validate():
 
208
        """Return True if the config is valid for the schema.
 
209
 
 
210
        :raise `ConfigErrors`: if the are errors. A list of all schema
 
211
            problems can be retrieved via the errors property.
 
212
        """
 
213
 
 
214
    def push(conf_name, conf_data):
 
215
        """Overlay the config with unparsed config data.
 
216
 
 
217
        :param conf_name: the name of the config.
 
218
        :param conf_data: a string of unparsed config data.
 
219
 
 
220
        This method appends the parsed `IConfigData` to the overlays property.
 
221
        """
 
222
 
 
223
    def pop(conf_name):
 
224
        """Remove conf_name from the overlays stack.
 
225
 
 
226
        :param conf_name: the name of the `IConfigData` to remove.
 
227
        :return: the tuple of `IConfigData` that was removed from overlays.
 
228
        :raise NoConfigError: if no `IConfigData` has the conf_name.
 
229
 
 
230
        This method removes the named ConfigData from the stack; ConfigData
 
231
        above the named ConfigData are removed too.
 
232
        """
 
233
 
 
234
 
 
235
class ICategory(Interface):
 
236
    """A group of related sections.
 
237
 
 
238
    The sections within a category are access as attributes of the
 
239
    `ICategory`.
 
240
    """
 
241
 
 
242
    def __getattr__(name):
 
243
        """Return the named section.
 
244
 
 
245
        :name: a section name.
 
246
        :return: the matching `ISection`.
 
247
        :raise: AttributeError if there is no section with the name.
 
248
        """