/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/config.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-22 00:56:52 UTC
  • mfrom: (6621.2.26 py3_pokes)
  • Revision ID: jelmer@jelmer.uk-20170522005652-yjahcr9hwmjkno7n
Merge Python3 porting work ('py3 pokes')

Show diffs side-by-side

added added

removed removed

Lines of Context:
73
73
"""
74
74
 
75
75
from __future__ import absolute_import
76
 
from cStringIO import StringIO
77
76
import os
78
77
import sys
79
78
 
 
79
import configobj
 
80
 
80
81
import breezy
81
 
from breezy.decorators import needs_write_lock
82
 
from breezy.lazy_import import lazy_import
 
82
from .decorators import needs_write_lock
 
83
from .lazy_import import lazy_import
83
84
lazy_import(globals(), """
84
85
import base64
85
86
import fnmatch
104
105
    win32utils,
105
106
    )
106
107
from breezy.i18n import gettext
107
 
from breezy.util.configobj import configobj
108
108
""")
109
 
from breezy import (
 
109
from . import (
110
110
    commands,
111
111
    hooks,
112
112
    lazy_regex,
113
113
    registry,
114
114
    )
115
 
from breezy.symbol_versioning import (
 
115
from .sixish import (
 
116
    binary_type,
 
117
    BytesIO,
 
118
    text_type,
 
119
    string_types,
 
120
    )
 
121
from .symbol_versioning import (
116
122
    deprecated_in,
117
123
    deprecated_method,
118
124
    )
176
182
                     % signature_string)
177
183
 
178
184
 
 
185
def _has_decode_bug():
 
186
    """True if configobj will fail to decode to unicode on Python 2."""
 
187
    if sys.version_info > (3,):
 
188
        return False
 
189
    conf = configobj.ConfigObj()
 
190
    decode = getattr(conf, "_decode", None)
 
191
    if decode:
 
192
        result = decode(b"\xc2\xa7", "utf-8")
 
193
        if isinstance(result[0], str):
 
194
            return True
 
195
    return False
 
196
 
 
197
 
 
198
def _has_triplequote_bug():
 
199
    """True if triple quote logic is reversed, see lp:710410."""
 
200
    conf = configobj.ConfigObj()
 
201
    quote = getattr(conf, "_get_triple_quote", None)
 
202
    if quote and quote('"""') != "'''":
 
203
        return True
 
204
    return False
 
205
 
 
206
 
179
207
class ConfigObj(configobj.ConfigObj):
180
208
 
181
209
    def __init__(self, infile=None, **kwargs):
184
212
                                        interpolation=False,
185
213
                                        **kwargs)
186
214
 
 
215
    if _has_decode_bug():
 
216
        def _decode(self, infile, encoding):
 
217
            if isinstance(infile, str) and encoding:
 
218
                return infile.decode(encoding).splitlines(True)
 
219
            return super(ConfigObj, self)._decode(infile, encoding)
 
220
 
 
221
    if _has_triplequote_bug():
 
222
        def _get_triple_quote(self, value):
 
223
            quot = super(ConfigObj, self)._get_triple_quote(value)
 
224
            if quot == configobj.tdquot:
 
225
                return configobj.tsquot
 
226
            return configobj.tdquot
 
227
 
187
228
    def get_bool(self, section, key):
188
229
        return self[section].as_bool(key)
189
230
 
398
439
            otherwise.
399
440
        """
400
441
        l = self.get_user_option(option_name, expand=expand)
401
 
        if isinstance(l, (str, unicode)):
 
442
        if isinstance(l, string_types):
402
443
            # A single value, most probably the user forgot (or didn't care to
403
444
            # add) the final ','
404
445
            l = [l]
736
777
        return conf
737
778
 
738
779
    def _create_from_string(self, str_or_unicode, save):
739
 
        self._content = StringIO(str_or_unicode.encode('utf-8'))
 
780
        self._content = BytesIO(str_or_unicode.encode('utf-8'))
740
781
        # Some tests use in-memory configs, some other always need the config
741
782
        # file to exist on disk.
742
783
        if save:
759
800
            co_input = self.file_name
760
801
        try:
761
802
            self._parser = ConfigObj(co_input, encoding='utf-8')
762
 
        except configobj.ConfigObjError, e:
 
803
        except configobj.ConfigObjError as e:
763
804
            raise errors.ParseConfigError(e.errors, e.config.filename)
764
805
        except UnicodeDecodeError:
765
806
            raise errors.ConfigContentError(self.file_name)
1195
1236
 
1196
1237
    def _get_matching_sections(self):
1197
1238
        """Return an ordered list of section names matching this location."""
1198
 
        matches = list(_iter_for_location_by_parts(self._get_parser(),
1199
 
                                                   self.location))
1200
1239
        # put the longest (aka more specific) locations first
1201
 
        matches.sort(
1202
 
            key=lambda (section, extra_path, length): (length, section),
 
1240
        matches = sorted(
 
1241
            _iter_for_location_by_parts(self._get_parser(), self.location),
 
1242
            key=lambda match: (match[2], match[0]),
1203
1243
            reverse=True)
1204
1244
        for (section, extra_path, length) in matches:
1205
1245
            yield section, extra_path
1605
1645
        return None
1606
1646
    try:
1607
1647
        f = open(mailname_file)
1608
 
    except (IOError, OSError), e:
 
1648
    except (IOError, OSError) as e:
1609
1649
        return None
1610
1650
    try:
1611
1651
        domain = f.readline().strip()
1672
1712
        try:
1673
1713
            encoding = osutils.get_user_encoding()
1674
1714
            gecos = w.pw_gecos.decode(encoding)
1675
 
        except UnicodeError, e:
 
1715
        except UnicodeError as e:
1676
1716
            trace.mutter("cannot decode passwd entry %s" % w)
1677
1717
            return None, None
1678
1718
    try:
1679
1719
        username = w.pw_name.decode(encoding)
1680
 
    except UnicodeError, e:
 
1720
    except UnicodeError as e:
1681
1721
        trace.mutter("cannot decode passwd entry %s" % w)
1682
1722
        return None, None
1683
1723
 
1784
1824
            # Note: the encoding below declares that the file itself is utf-8
1785
1825
            # encoded, but the values in the ConfigObj are always Unicode.
1786
1826
            self._config = ConfigObj(self._input, encoding='utf-8')
1787
 
        except configobj.ConfigObjError, e:
 
1827
        except configobj.ConfigObjError as e:
1788
1828
            raise errors.ParseConfigError(e.errors, e.config.filename)
1789
1829
        except UnicodeError:
1790
1830
            raise errors.ConfigContentError(self._filename)
1843
1883
        """
1844
1884
        credentials = None
1845
1885
        for auth_def_name, auth_def in self._get_config().items():
1846
 
            if type(auth_def) is not configobj.Section:
 
1886
            if not isinstance(auth_def, configobj.Section):
1847
1887
                raise ValueError("%s defined outside a section" % auth_def_name)
1848
1888
 
1849
1889
            a_scheme, a_host, a_user, a_path = map(
2276
2316
 
2277
2317
    def _get_config_file(self):
2278
2318
        try:
2279
 
            f = StringIO(self._transport.get_bytes(self._filename))
 
2319
            f = BytesIO(self._transport.get_bytes(self._filename))
2280
2320
            for hook in OldConfigHooks['load']:
2281
2321
                hook(self)
2282
2322
            return f
2283
2323
        except errors.NoSuchFile:
2284
 
            return StringIO()
2285
 
        except errors.PermissionDenied, e:
 
2324
            return BytesIO()
 
2325
        except errors.PermissionDenied as e:
2286
2326
            trace.warning("Permission denied while trying to open "
2287
2327
                "configuration file %s.", urlutils.unescape_for_display(
2288
2328
                urlutils.join(self._transport.base, self._filename), "utf-8"))
2289
 
            return StringIO()
 
2329
            return BytesIO()
2290
2330
 
2291
2331
    def _external_url(self):
2292
2332
        return urlutils.join(self._transport.external_url(), self._filename)
2296
2336
        try:
2297
2337
            try:
2298
2338
                conf = ConfigObj(f, encoding='utf-8')
2299
 
            except configobj.ConfigObjError, e:
 
2339
            except configobj.ConfigObjError as e:
2300
2340
                raise errors.ParseConfigError(e.errors, self._external_url())
2301
2341
            except UnicodeDecodeError:
2302
2342
                raise errors.ConfigContentError(self._external_url())
2305
2345
        return conf
2306
2346
 
2307
2347
    def _set_configobj(self, configobj):
2308
 
        out_file = StringIO()
 
2348
        out_file = BytesIO()
2309
2349
        configobj.write(out_file)
2310
2350
        out_file.seek(0)
2311
2351
        self._transport.put_file(self._filename, out_file)
2377
2417
                raise AssertionError(
2378
2418
                    'Only empty lists are supported as default values')
2379
2419
            self.default = u','
2380
 
        elif isinstance(default, (str, unicode, bool, int, float)):
 
2420
        elif isinstance(default, (binary_type, text_type, bool, int, float)):
2381
2421
            # Rely on python to convert strings, booleans and integers
2382
2422
            self.default = u'%s' % (default,)
2383
2423
        elif callable(default):
2442
2482
            # Otherwise, fallback to the value defined at registration
2443
2483
            if callable(self.default):
2444
2484
                value = self.default()
2445
 
                if not isinstance(value, unicode):
 
2485
                if not isinstance(value, text_type):
2446
2486
                    raise AssertionError(
2447
2487
                        "Callable default value for '%s' should be unicode"
2448
2488
                        % (self.name))
2525
2565
            invalid=invalid, unquote=False)
2526
2566
 
2527
2567
    def from_unicode(self, unicode_str):
2528
 
        if not isinstance(unicode_str, basestring):
 
2568
        if not isinstance(unicode_str, string_types):
2529
2569
            raise TypeError
2530
2570
        # Now inject our string directly as unicode. All callers got their
2531
2571
        # value from configobj, so values that need to be quoted are already
2533
2573
        _list_converter_config.reset()
2534
2574
        _list_converter_config._parse([u"list=%s" % (unicode_str,)])
2535
2575
        maybe_list = _list_converter_config['list']
2536
 
        if isinstance(maybe_list, basestring):
 
2576
        if isinstance(maybe_list, string_types):
2537
2577
            if maybe_list:
2538
2578
                # A single value, most probably the user forgot (or didn't care
2539
2579
                # to add) the final ','
2565
2605
        self.registry = registry
2566
2606
 
2567
2607
    def from_unicode(self, unicode_str):
2568
 
        if not isinstance(unicode_str, basestring):
 
2608
        if not isinstance(unicode_str, string_types):
2569
2609
            raise TypeError
2570
2610
        try:
2571
2611
            return self.registry.get(unicode_str)
3255
3295
        """
3256
3296
        if self.is_loaded():
3257
3297
            raise AssertionError('Already loaded: %r' % (self._config_obj,))
3258
 
        co_input = StringIO(bytes)
 
3298
        co_input = BytesIO(bytes)
3259
3299
        try:
3260
3300
            # The config files are always stored utf8-encoded
3261
3301
            self._config_obj = ConfigObj(co_input, encoding='utf-8',
3262
3302
                                         list_values=False)
3263
 
        except configobj.ConfigObjError, e:
 
3303
        except configobj.ConfigObjError as e:
3264
3304
            self._config_obj = None
3265
3305
            raise errors.ParseConfigError(e.errors, self.external_url())
3266
3306
        except UnicodeDecodeError:
3282
3322
        if not self.is_loaded():
3283
3323
            # Nothing to save
3284
3324
            return
3285
 
        out = StringIO()
 
3325
        out = BytesIO()
3286
3326
        self._config_obj.write(out)
3287
3327
        self._save_content(out.getvalue())
3288
3328
        for hook in ConfigHooks['save']:
3335
3375
            self._config_obj.list_values = False
3336
3376
 
3337
3377
    def unquote(self, value):
3338
 
        if value and isinstance(value, basestring):
 
3378
        if value and isinstance(value, string_types):
3339
3379
            # _unquote doesn't handle None nor empty strings nor anything that
3340
3380
            # is not a string, really.
3341
3381
            value = self._config_obj._unquote(value)
3384
3424
        # The following will do in the interim but maybe we don't want to
3385
3425
        # expose a path here but rather a config ID and its associated
3386
3426
        # object </hand wawe>.
3387
 
        return urlutils.join(self.transport.external_url(), self.file_name)
 
3427
        return urlutils.join(self.transport.external_url(), self.file_name.encode("ascii"))
3388
3428
 
3389
3429
 
3390
3430
# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
3662
3702
 
3663
3703
    def get_sections(self):
3664
3704
        # Override the default implementation as we want to change the order
3665
 
        matching_sections = self._get_matching_sections()
3666
3705
        # We want the longest (aka more specific) locations first
3667
 
        sections = sorted(matching_sections,
3668
 
                          key=lambda (length, section): (length, section.id),
 
3706
        sections = sorted(self._get_matching_sections(),
 
3707
                          key=lambda match: (match[0], match[1].id),
3669
3708
                          reverse=True)
3670
3709
        # Sections mentioning 'ignore_parents' restrict the selection
3671
3710
        for _, section in sections:
3749
3788
            # None or ends up being None during expansion or conversion.
3750
3789
            if val is not None:
3751
3790
                if expand:
3752
 
                    if isinstance(val, basestring):
 
3791
                    if isinstance(val, string_types):
3753
3792
                        val = self._expand_options_in_string(val)
3754
3793
                    else:
3755
3794
                        trace.warning('Cannot expand "%s":'
3901
3940
            global _shared_stores_at_exit_installed
3902
3941
            stores = _shared_stores
3903
3942
            def save_config_changes():
3904
 
                for k, store in stores.iteritems():
 
3943
                for k, store in stores.items():
3905
3944
                    store.save_changes()
3906
3945
            if not _shared_stores_at_exit_installed:
3907
3946
                # FIXME: Ugly hack waiting for library_state to always be
4154
4193
        # http://pad.lv/788991 -- vila 20101115
4155
4194
        commands.Option('scope', help='Reduce the scope to the specified'
4156
4195
                        ' configuration file.',
4157
 
                        type=unicode),
 
4196
                        type=text_type),
4158
4197
        commands.Option('all',
4159
4198
            help='Display all the defined values for the matching options.',
4160
4199
            ),