~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
import os
66
66
import sys
67
67
 
 
68
from bzrlib.decorators import needs_write_lock
68
69
from bzrlib.lazy_import import lazy_import
69
70
lazy_import(globals(), """
70
71
import errno
74
75
 
75
76
import bzrlib
76
77
from bzrlib import (
 
78
    atomicfile,
77
79
    debug,
78
80
    errors,
 
81
    lockdir,
79
82
    mail_client,
80
83
    osutils,
81
84
    registry,
82
85
    symbol_versioning,
83
86
    trace,
 
87
    transport,
84
88
    ui,
85
89
    urlutils,
86
90
    win32utils,
351
355
class IniBasedConfig(Config):
352
356
    """A configuration policy that draws from ini files."""
353
357
 
354
 
    def __init__(self, get_filename):
 
358
    def __init__(self, get_filename=symbol_versioning.DEPRECATED_PARAMETER,
 
359
                 file_name=None):
 
360
        """Base class for configuration files using an ini-like syntax.
 
361
 
 
362
        :param file_name: The configuration file path.
 
363
        """
355
364
        super(IniBasedConfig, self).__init__()
356
 
        self._get_filename = get_filename
 
365
        self.file_name = file_name
 
366
        if symbol_versioning.deprecated_passed(get_filename):
 
367
            symbol_versioning.warn(
 
368
                'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
369
                ' Use file_name instead.',
 
370
                DeprecationWarning,
 
371
                stacklevel=2)
 
372
            if get_filename is not None:
 
373
                self.file_name = get_filename()
 
374
        else:
 
375
            self.file_name = file_name
 
376
        self._content = None
357
377
        self._parser = None
358
378
 
359
 
    def _get_parser(self, file=None):
 
379
    @classmethod
 
380
    def from_string(cls, str_or_unicode, file_name=None, save=False):
 
381
        """Create a config object from a string.
 
382
 
 
383
        :param str_or_unicode: A string representing the file content. This will
 
384
            be utf-8 encoded.
 
385
 
 
386
        :param file_name: The configuration file path.
 
387
 
 
388
        :param _save: Whether the file should be saved upon creation.
 
389
        """
 
390
        conf = cls(file_name=file_name)
 
391
        conf._create_from_string(str_or_unicode, save)
 
392
        return conf
 
393
 
 
394
    def _create_from_string(self, str_or_unicode, save):
 
395
        self._content = StringIO(str_or_unicode.encode('utf-8'))
 
396
        # Some tests use in-memory configs, some other always need the config
 
397
        # file to exist on disk.
 
398
        if save:
 
399
            self._write_config_file()
 
400
 
 
401
    def _get_parser(self, file=symbol_versioning.DEPRECATED_PARAMETER):
360
402
        if self._parser is not None:
361
403
            return self._parser
362
 
        if file is None:
363
 
            input = self._get_filename()
 
404
        if symbol_versioning.deprecated_passed(file):
 
405
            symbol_versioning.warn(
 
406
                'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
407
                ' Use IniBasedConfig(_content=xxx) instead.',
 
408
                DeprecationWarning,
 
409
                stacklevel=2)
 
410
        if self._content is not None:
 
411
            co_input = self._content
 
412
        elif self.file_name is None:
 
413
            raise AssertionError('We have no content to create the config')
364
414
        else:
365
 
            input = file
 
415
            co_input = self.file_name
366
416
        try:
367
 
            self._parser = ConfigObj(input, encoding='utf-8')
 
417
            self._parser = ConfigObj(co_input, encoding='utf-8')
368
418
        except configobj.ConfigObjError, e:
369
419
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
420
        # Make sure self.reload() will use the right file name
 
421
        self._parser.filename = self.file_name
370
422
        return self._parser
371
423
 
 
424
    def reload(self):
 
425
        """Reload the config file from disk."""
 
426
        if self.file_name is None:
 
427
            raise AssertionError('We need a file name to reload the config')
 
428
        if self._parser is not None:
 
429
            self._parser.reload()
 
430
 
372
431
    def _get_matching_sections(self):
373
432
        """Return an ordered list of (section_name, extra_path) pairs.
374
433
 
478
537
        return self.get_user_option('nickname')
479
538
 
480
539
    def _write_config_file(self):
481
 
        f = file(self._get_filename(), "wb")
482
 
        try:
483
 
            osutils.copy_ownership_from_path(f.name)
484
 
            self._get_parser().write(f)
485
 
        finally:
486
 
            f.close()
487
 
 
488
 
 
489
 
class GlobalConfig(IniBasedConfig):
 
540
        if self.file_name is None:
 
541
            raise AssertionError('We cannot save, self.file_name is None')
 
542
        conf_dir = os.path.dirname(self.file_name)
 
543
        ensure_config_dir_exists(conf_dir)
 
544
        atomic_file = atomicfile.AtomicFile(self.file_name)
 
545
        self._get_parser().write(atomic_file)
 
546
        atomic_file.commit()
 
547
        atomic_file.close()
 
548
        osutils.copy_ownership_from_path(self.file_name)
 
549
 
 
550
 
 
551
class LockableConfig(IniBasedConfig):
 
552
    """A configuration needing explicit locking for access.
 
553
 
 
554
    If several processes try to write the config file, the accesses need to be
 
555
    serialized.
 
556
 
 
557
    Daughter classes should decorate all methods that update a config with the
 
558
    ``@needs_write_lock`` decorator (they call, directly or indirectly, the
 
559
    ``_write_config_file()`` method. These methods (typically ``set_option()``
 
560
    and variants must reload the config file from disk before calling
 
561
    ``_write_config_file()``), this can be achieved by calling the
 
562
    ``self.reload()`` method. Note that the lock scope should cover both the
 
563
    reading and the writing of the config file which is why the decorator can't
 
564
    be applied to ``_write_config_file()`` only.
 
565
 
 
566
    This should be enough to implement the following logic:
 
567
    - lock for exclusive write access,
 
568
    - reload the config file from disk,
 
569
    - set the new value
 
570
    - unlock
 
571
 
 
572
    This logic guarantees that a writer can update a value without erasing an
 
573
    update made by another writer.
 
574
    """
 
575
 
 
576
    lock_name = 'lock'
 
577
 
 
578
    def __init__(self, file_name):
 
579
        super(LockableConfig, self).__init__(file_name=file_name)
 
580
        self.dir = osutils.dirname(osutils.safe_unicode(self.file_name))
 
581
        self.transport = transport.get_transport(self.dir)
 
582
        self._lock = lockdir.LockDir(self.transport, 'lock')
 
583
 
 
584
    def _create_from_string(self, unicode_bytes, save):
 
585
        super(LockableConfig, self)._create_from_string(unicode_bytes, False)
 
586
        if save:
 
587
            # We need to handle the saving here (as opposed to IniBasedConfig)
 
588
            # to be able to lock
 
589
            self.lock_write()
 
590
            self._write_config_file()
 
591
            self.unlock()
 
592
 
 
593
    def lock_write(self, token=None):
 
594
        """Takes a write lock in the directory containing the config file.
 
595
 
 
596
        If the directory doesn't exist it is created.
 
597
        """
 
598
        ensure_config_dir_exists(self.dir)
 
599
        return self._lock.lock_write(token)
 
600
 
 
601
    def unlock(self):
 
602
        self._lock.unlock()
 
603
 
 
604
    def break_lock(self):
 
605
        self._lock.break_lock()
 
606
 
 
607
    def _write_config_file(self):
 
608
        if self._lock is None or not self._lock.is_held:
 
609
            # NB: if the following exception is raised it probably means a
 
610
            # missing @needs_write_lock decorator on one of the callers.
 
611
            raise errors.ObjectNotLocked(self)
 
612
        super(LockableConfig, self)._write_config_file()
 
613
 
 
614
 
 
615
class GlobalConfig(LockableConfig):
490
616
    """The configuration that should be used for a specific location."""
491
617
 
 
618
    def __init__(self):
 
619
        super(GlobalConfig, self).__init__(file_name=config_filename())
 
620
 
 
621
    @classmethod
 
622
    def from_string(cls, str_or_unicode, save=False):
 
623
        """Create a config object from a string.
 
624
 
 
625
        :param str_or_unicode: A string representing the file content. This
 
626
            will be utf-8 encoded.
 
627
 
 
628
        :param save: Whether the file should be saved upon creation.
 
629
        """
 
630
        conf = cls()
 
631
        conf._create_from_string(str_or_unicode, save)
 
632
        return conf
 
633
 
492
634
    def get_editor(self):
493
635
        return self._get_user_option('editor')
494
636
 
495
 
    def __init__(self):
496
 
        super(GlobalConfig, self).__init__(config_filename)
497
 
 
 
637
    @needs_write_lock
498
638
    def set_user_option(self, option, value):
499
639
        """Save option and its value in the configuration."""
500
640
        self._set_option(option, value, 'DEFAULT')
506
646
        else:
507
647
            return {}
508
648
 
 
649
    @needs_write_lock
509
650
    def set_alias(self, alias_name, alias_command):
510
651
        """Save the alias in the configuration."""
511
652
        self._set_option(alias_name, alias_command, 'ALIASES')
512
653
 
 
654
    @needs_write_lock
513
655
    def unset_alias(self, alias_name):
514
656
        """Unset an existing alias."""
 
657
        self.reload()
515
658
        aliases = self._get_parser().get('ALIASES')
516
659
        if not aliases or alias_name not in aliases:
517
660
            raise errors.NoSuchAlias(alias_name)
519
662
        self._write_config_file()
520
663
 
521
664
    def _set_option(self, option, value, section):
522
 
        # FIXME: RBC 20051029 This should refresh the parser and also take a
523
 
        # file lock on bazaar.conf.
524
 
        conf_dir = os.path.dirname(self._get_filename())
525
 
        ensure_config_dir_exists(conf_dir)
 
665
        self.reload()
526
666
        self._get_parser().setdefault(section, {})[option] = value
527
667
        self._write_config_file()
528
668
 
529
669
 
530
 
class LocationConfig(IniBasedConfig):
 
670
class LocationConfig(LockableConfig):
531
671
    """A configuration object that gives the policy for a location."""
532
672
 
533
673
    def __init__(self, location):
534
 
        name_generator = locations_config_filename
535
 
        if (not os.path.exists(name_generator()) and
536
 
                os.path.exists(branches_config_filename())):
537
 
            if sys.platform == 'win32':
538
 
                trace.warning('Please rename %s to %s'
539
 
                              % (branches_config_filename(),
540
 
                                 locations_config_filename()))
541
 
            else:
542
 
                trace.warning('Please rename ~/.bazaar/branches.conf'
543
 
                              ' to ~/.bazaar/locations.conf')
544
 
            name_generator = branches_config_filename
545
 
        super(LocationConfig, self).__init__(name_generator)
 
674
        super(LocationConfig, self).__init__(
 
675
            file_name=locations_config_filename())
546
676
        # local file locations are looked up by local path, rather than
547
677
        # by file url. This is because the config file is a user
548
678
        # file, and we would rather not expose the user to file urls.
550
680
            location = urlutils.local_path_from_url(location)
551
681
        self.location = location
552
682
 
 
683
    @classmethod
 
684
    def from_string(cls, str_or_unicode, location, save=False):
 
685
        """Create a config object from a string.
 
686
 
 
687
        :param str_or_unicode: A string representing the file content. This will
 
688
            be utf-8 encoded.
 
689
 
 
690
        :param location: The location url to filter the configuration.
 
691
 
 
692
        :param save: Whether the file should be saved upon creation.
 
693
        """
 
694
        conf = cls(location)
 
695
        conf._create_from_string(str_or_unicode, save)
 
696
        return conf
 
697
 
553
698
    def _get_matching_sections(self):
554
699
        """Return an ordered list of section names matching this location."""
555
700
        sections = self._get_parser()
643
788
            if policy_key in self._get_parser()[section]:
644
789
                del self._get_parser()[section][policy_key]
645
790
 
 
791
    @needs_write_lock
646
792
    def set_user_option(self, option, value, store=STORE_LOCATION):
647
793
        """Save option and its value in the configuration."""
648
794
        if store not in [STORE_LOCATION,
650
796
                         STORE_LOCATION_APPENDPATH]:
651
797
            raise ValueError('bad storage policy %r for %r' %
652
798
                (store, option))
653
 
        # FIXME: RBC 20051029 This should refresh the parser and also take a
654
 
        # file lock on locations.conf.
655
 
        conf_dir = os.path.dirname(self._get_filename())
656
 
        ensure_config_dir_exists(conf_dir)
 
799
        self.reload()
657
800
        location = self.location
658
801
        if location.endswith('/'):
659
802
            location = location[:-1]
660
 
        if (not location in self._get_parser() and
661
 
            not location + '/' in self._get_parser()):
662
 
            self._get_parser()[location]={}
663
 
        elif location + '/' in self._get_parser():
 
803
        parser = self._get_parser()
 
804
        if not location in parser and not location + '/' in parser:
 
805
            parser[location] = {}
 
806
        elif location + '/' in parser:
664
807
            location = location + '/'
665
 
        self._get_parser()[location][option]=value
 
808
        parser[location][option]=value
666
809
        # the allowed values of store match the config policies
667
810
        self._set_option_policy(location, option, store)
668
811
        self._write_config_file()
671
814
class BranchConfig(Config):
672
815
    """A configuration object giving the policy for a branch."""
673
816
 
 
817
    def __init__(self, branch):
 
818
        super(BranchConfig, self).__init__()
 
819
        self._location_config = None
 
820
        self._branch_data_config = None
 
821
        self._global_config = None
 
822
        self.branch = branch
 
823
        self.option_sources = (self._get_location_config,
 
824
                               self._get_branch_data_config,
 
825
                               self._get_global_config)
 
826
 
674
827
    def _get_branch_data_config(self):
675
828
        if self._branch_data_config is None:
676
829
            self._branch_data_config = TreeConfig(self.branch)
774
927
        """See Config.gpg_signing_command."""
775
928
        return self._get_safe_value('_gpg_signing_command')
776
929
 
777
 
    def __init__(self, branch):
778
 
        super(BranchConfig, self).__init__()
779
 
        self._location_config = None
780
 
        self._branch_data_config = None
781
 
        self._global_config = None
782
 
        self.branch = branch
783
 
        self.option_sources = (self._get_location_config,
784
 
                               self._get_branch_data_config,
785
 
                               self._get_global_config)
786
 
 
787
930
    def _post_commit(self):
788
931
        """See Config.post_commit."""
789
932
        return self._get_safe_value('_post_commit')
853
996
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
854
997
 
855
998
 
856
 
def branches_config_filename():
857
 
    """Return per-user configuration ini file filename."""
858
 
    return osutils.pathjoin(config_dir(), 'branches.conf')
859
 
 
860
 
 
861
999
def locations_config_filename():
862
1000
    """Return per-user configuration ini file filename."""
863
1001
    return osutils.pathjoin(config_dir(), 'locations.conf')