1
# Copyright 2009 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
# pylint: disable-msg=E0211,E0213,W0231
5
"""Interfaces for process configuration.."""
17
'InvalidSectionNameError',
23
'RedefinedSectionError',
25
'UnknownSectionError']
27
from zope.interface import Interface, Attribute
30
class ConfigSchemaError(Exception):
31
"""A base class of all `IConfigSchema` errors."""
34
class RedefinedKeyError(ConfigSchemaError):
35
"""A key in a section cannot be redefined."""
38
class RedefinedSectionError(ConfigSchemaError):
39
"""A section in a config file cannot be redefined."""
42
class InvalidSectionNameError(ConfigSchemaError):
43
"""The section name contains more than one category."""
46
class NoCategoryError(LookupError):
47
"""No `ISectionSchema`s belong to the category name."""
50
class UnknownSectionError(ConfigSchemaError):
51
"""The config has a section that is not in the schema."""
54
class UnknownKeyError(ConfigSchemaError):
55
"""The section has a key that is not in the schema."""
57
class NoConfigError(ConfigSchemaError):
58
"""No config has the name."""
60
class ConfigErrors(ConfigSchemaError):
61
"""The errors in a Config.
63
The list of errors can be accessed via the errors attribute.
66
def __init__(self, message, errors=None):
67
"""Initialize the error with a message and errors.
69
:param message: a message string
70
:param errors: a list of errors in the config, or None
72
self.message = message
76
return '%s: %s' % (self.__class__.__name__, self.message)
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.")
87
"""Iterate over the keys."""
89
def __contains__(name):
90
"""Return True or False if name is a key."""
93
"""Return the default value of the key.
95
:raise `KeyError`: if the key does not exist.
99
class ISection(ISectionSchema):
100
"""Defines the values for a configuration group."""
101
schema = Attribute("The ISectionSchema that defines this ISection.")
103
def __getattr__(name):
104
"""Return the named key.
107
:return: the value of the matching key.
108
:raise: AttributeError if there is no key with the name.
111
class IConfigLoader(Interface):
112
"""A configuration file loader."""
115
"""Load a configuration from the file at filename."""
117
def loadFile(source_file, filename=None):
118
"""Load a configuration from the open source_file.
120
:param source_file: A file-like object that supports read() and
122
:param filename: The name of the configuration. If filename is None,
123
The name will be taken from source_file.name.
127
class IConfigSchema(Interface):
128
"""A process configuration schema.
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.
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.')
143
"""Iterate over the `ISectionSchema`s."""
145
def __contains__(name):
146
"""Return True or False if the name matches a `ISectionSchema`."""
148
def __getitem__(name):
149
"""Return the `ISectionSchema` with the matching name.
151
:raise `NoSectionError`: if the no ISectionSchema has the name.
154
def getByCategory(name):
155
"""Return a list of ISectionSchemas that belong to the category name.
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
163
:raise `CategoryNotFound`: if no sections have a name that starts
164
with the category name.
168
class IConfigData(IConfigSchema):
169
"""A process configuration.
171
See `IConfigSchema` for more information about the config file format.
175
class IStackableConfig(IConfigSchema):
176
"""A configuration that is built from configs that extend each other.
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.
184
A config file declares that is extends another using the 'extends' key
185
in the 'meta' section of the config data file:
189
The push() and pop() methods can be used to test processes where the
190
test environment must be configured differently.
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.")
198
def __getattr__(name):
199
"""Return the named section.
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
208
"""Return True if the config is valid for the schema.
210
:raise `ConfigErrors`: if the are errors. A list of all schema
211
problems can be retrieved via the errors property.
214
def push(conf_name, conf_data):
215
"""Overlay the config with unparsed config data.
217
:param conf_name: the name of the config.
218
:param conf_data: a string of unparsed config data.
220
This method appends the parsed `IConfigData` to the overlays property.
224
"""Remove conf_name from the overlays stack.
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.
230
This method removes the named ConfigData from the stack; ConfigData
231
above the named ConfigData are removed too.
235
class ICategory(Interface):
236
"""A group of related sections.
238
The sections within a category are access as attributes of the
242
def __getattr__(name):
243
"""Return the named section.
245
:name: a section name.
246
:return: the matching `ISection`.
247
:raise: AttributeError if there is no section with the name.