~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

Merge config-concrete-stores into config-section-matchers resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
2138
2138
class Store(object):
2139
2139
    """Abstract interface to persistent storage for configuration options."""
2140
2140
 
2141
 
    def __init__(self):
2142
 
        self.loaded = False
 
2141
    readonly_section_class = ReadOnlySection
 
2142
    mutable_section_class = MutableSection
 
2143
 
 
2144
    @property
 
2145
    def loaded(self):
 
2146
        raise NotImplementedError(self.loaded)
2143
2147
 
2144
2148
    def load(self):
2145
2149
        raise NotImplementedError(self.load)
2146
2150
 
 
2151
    def _load_from_string(self, str_or_unicode):
 
2152
        """Create a store from a string in configobj syntax.
 
2153
 
 
2154
        :param str_or_unicode: A string representing the file content. This will
 
2155
            be encoded to suit store needs internally.
 
2156
 
 
2157
        This is for tests and should not be used in production unless a
 
2158
        convincing use case can be demonstrated :)
 
2159
        """
 
2160
        raise NotImplementedError(self._load_from_string)
 
2161
 
2147
2162
    def save(self):
2148
2163
        raise NotImplementedError(self.save)
2149
2164
 
 
2165
    def external_url(self):
 
2166
        raise NotImplementedError(self.external_url)
 
2167
 
2150
2168
    def get_sections(self):
2151
2169
        """Returns an ordered iterable of existing sections.
2152
2170
 
2174
2192
        super(ConfigObjStore, self).__init__()
2175
2193
        self.transport = transport
2176
2194
        self.file_name = file_name
2177
 
        # No transient content is known initially
2178
 
        self._content = None
2179
 
 
2180
 
    @classmethod
2181
 
    def from_string(cls, str_or_unicode, transport, file_name):
 
2195
        self._config_obj = None
 
2196
 
 
2197
    @property
 
2198
    def loaded(self):
 
2199
        return self._config_obj != None
 
2200
 
 
2201
    def load(self):
 
2202
        """Load the store from the associated file."""
 
2203
        if self.loaded:
 
2204
            return
 
2205
        content = self.transport.get_bytes(self.file_name)
 
2206
        self._load_from_string(content)
 
2207
 
 
2208
    def _load_from_string(self, str_or_unicode):
2182
2209
        """Create a config store from a string.
2183
2210
 
2184
2211
        :param str_or_unicode: A string representing the file content. This will
2185
2212
            be utf-8 encoded internally.
2186
2213
 
2187
 
        :param transport: The transport object where the config file is located.
2188
 
 
2189
 
        :param file_name: The configuration file basename.
2190
 
        """
2191
 
        conf = cls(transport=transport, file_name=file_name)
2192
 
        conf._create_from_string(str_or_unicode)
2193
 
        return conf
2194
 
 
2195
 
    def _create_from_string(self, str_or_unicode):
2196
 
        # We just keep the content waiting for load() to be called when needed
2197
 
        self._content = StringIO(str_or_unicode.encode('utf-8'))
2198
 
 
2199
 
    def load(self, allow_no_such_file=False):
2200
 
        """Load the store from the associated file.
2201
 
 
2202
 
        :param allow_no_such_file: Swallow the NoSuchFile exception if True.
2203
 
            This allows delayed loading when creating the first option ever.
 
2214
        This is for tests and should not be used in production unless a
 
2215
        convincing use case can be demonstrated :)
2204
2216
        """
2205
2217
        if self.loaded:
2206
 
            return
2207
 
        if self._content is not None:
2208
 
            co_input = self._content
2209
 
        else:
2210
 
            try:
2211
 
                content = self.transport.get_bytes(self.file_name)
2212
 
            except errors.NoSuchFile:
2213
 
                if allow_no_such_file:
2214
 
                    content = ''
2215
 
                else:
2216
 
                    raise
2217
 
            co_input =  StringIO(content)
 
2218
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
 
2219
        co_input = StringIO(str_or_unicode.encode('utf-8'))
2218
2220
        try:
2219
2221
            # The config files are always stored utf8-encoded
2220
2222
            self._config_obj = ConfigObj(co_input, encoding='utf-8')
2221
2223
        except configobj.ConfigObjError, e:
2222
 
            # FIXME: external_url should really accepts an optional relpath
2223
 
            # parameter (bug #750169) :-/ -- vila 2011-04-04
2224
 
            # The following will do in the interim but maybe we don't want to
2225
 
            # expose a path here but rather a config ID and its associated
2226
 
            # object </hand wawe>.
2227
 
            file_path = os.path.join(self.transport.external_url(),
2228
 
                                     self.file_name)
2229
 
            raise errors.ParseConfigError(e.errors, file_path)
2230
 
        self.loaded = True
 
2224
            self._config_obj = None
 
2225
            raise errors.ParseConfigError(e.errors, self.external_url())
2231
2226
 
2232
2227
    def save(self):
 
2228
        if not self.loaded:
 
2229
            # Nothing to save
 
2230
            return
2233
2231
        out = StringIO()
2234
2232
        self._config_obj.write(out)
2235
2233
        self.transport.put_bytes(self.file_name, out.getvalue())
2236
 
        # We don't need the transient content anymore
2237
 
        self._content = None
 
2234
 
 
2235
    def external_url(self):
 
2236
        # FIXME: external_url should really accepts an optional relpath
 
2237
        # parameter (bug #750169) :-/ -- vila 2011-04-04
 
2238
        # The following will do in the interim but maybe we don't want to
 
2239
        # expose a path here but rather a config ID and its associated
 
2240
        # object </hand wawe>.
 
2241
        return os.path.join(self.transport.external_url(), self.file_name)
2238
2242
 
2239
2243
    def get_sections(self):
2240
2244
        """Get the configobj section in the file order.
2245
2249
        self.load()
2246
2250
        cobj = self._config_obj
2247
2251
        if cobj.scalars:
2248
 
            yield ReadOnlySection(None, cobj)
 
2252
            yield self.readonly_section_class(None, cobj)
2249
2253
        for section_name in cobj.sections:
2250
 
            yield ReadOnlySection(section_name, cobj[section_name])
 
2254
            yield self.readonly_section_class(section_name, cobj[section_name])
2251
2255
 
2252
2256
    def get_mutable_section(self, section_name=None):
2253
2257
        # We need a loaded store
2254
 
        self.load(allow_no_such_file=True)
 
2258
        try:
 
2259
            self.load()
 
2260
        except errors.NoSuchFile:
 
2261
            # The file doesn't exist, let's pretend it was empty
 
2262
            self._load_from_string('')
2255
2263
        if section_name is None:
2256
2264
            section = self._config_obj
2257
2265
        else:
2258
2266
            section = self._config_obj.setdefault(section_name, {})
2259
 
        return MutableSection(section_name, section)
 
2267
        return self.mutable_section_class(section_name, section)
2260
2268
 
2261
2269
 
2262
2270
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
2319
2327
    def __init__(self, possible_transports=None):
2320
2328
        t = transport.get_transport(config_dir(),
2321
2329
                                    possible_transports=possible_transports)
2322
 
        super(LocationStore, self).__init__(transport, 'locations.conf')
 
2330
        super(LocationStore, self).__init__(t, 'locations.conf')
2323
2331
 
2324
2332
 
2325
2333
class BranchStore(ConfigObjStore):