diff options
Diffstat (limited to 'git/config.py')
-rw-r--r-- | git/config.py | 126 |
1 files changed, 63 insertions, 63 deletions
diff --git a/git/config.py b/git/config.py index 285ade6b..5ad69c6a 100644 --- a/git/config.py +++ b/git/config.py @@ -35,16 +35,16 @@ class MetaParserBuilder(type): if name in mutating_methods: method_with_values = set_dirty_and_flush_changes(method_with_values) # END mutating methods handling - + clsdict[name] = method_with_values # END for each name/method pair # END for each base # END if mutating methods configuration is set - + new_type = super(MetaParserBuilder, metacls).__new__(metacls, name, bases, clsdict) return new_type - - + + def needs_values(func): """Returns method assuring we read values (on demand) before we try to access them""" @@ -54,7 +54,7 @@ def needs_values(func): # END wrapper method assure_data_present.__name__ = func.__name__ return assure_data_present - + def set_dirty_and_flush_changes(non_const_func): """Return method that checks whether given non constant function may be called. If so, the instance will be set dirty. @@ -66,64 +66,64 @@ def set_dirty_and_flush_changes(non_const_func): # END wrapper method flush_changes.__name__ = non_const_func.__name__ return flush_changes - + class SectionConstraint(object): """Constrains a ConfigParser to only option commands which are constrained to always use the section we have been initialized with. - + It supports all ConfigParser methods that operate on an option""" __slots__ = ("_config", "_section_name") _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option", "remove_section", "remove_option", "options") - + def __init__(self, config, section): self._config = config self._section_name = section - + def __getattr__(self, attr): if attr in self._valid_attrs_: return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) return super(SectionConstraint,self).__getattribute__(attr) - + def _call_config(self, method, *args, **kwargs): """Call the configuration at the given method which must take a section name as first argument""" return getattr(self._config, method)(self._section_name, *args, **kwargs) - + @property def config(self): """return: Configparser instance we constrain""" return self._config - + class GitConfigParser(cp.RawConfigParser, object): """Implements specifics required to read git style configuration files. - + This variation behaves much like the git.config command such that the configuration will be read on demand based on the filepath given during initialization. - + The changes will automatically be written once the instance goes out of scope, but can be triggered manually as well. - + The configuration file will be locked if you intend to change values preventing other instances to write concurrently. - + :note: The config is case-sensitive even when queried, hence section and option names must match perfectly.""" __metaclass__ = MetaParserBuilder - - + + #{ Configuration # The lock type determines the type of lock to use in new configuration readers. # They must be compatible to the LockFile interface. # A suitable alternative would be the BlockingLockFile t_lock = LockFile re_comment = re.compile('^\s*[#;]') - + #} END configuration - + OPTCRE = re.compile( r'\s*(?P<option>[^:=\s][^:=]*)' # very permissive, incuding leading whitespace r'\s*(?P<vi>[:=])\s*' # any number of space/tab, @@ -132,18 +132,18 @@ class GitConfigParser(cp.RawConfigParser, object): # by any # space/tab r'(?P<value>.*)$' # everything up to eol ) - + # list of RawConfigParser methods able to change the instance _mutating_methods_ = ("add_section", "remove_section", "remove_option", "set") __slots__ = ("_sections", "_defaults", "_file_or_files", "_read_only","_is_initialized", '_lock') - + def __init__(self, file_or_files, read_only=True): """Initialize a configuration reader to read the given file_or_files and to possibly allow changes to it by setting read_only False - + :param file_or_files: A single file path or file objects or multiple of these - + :param read_only: If True, the ConfigParser may only read the data , but not change it. If False, only a single file path or file object may be given.""" @@ -152,34 +152,34 @@ class GitConfigParser(cp.RawConfigParser, object): # file back self._sections = OrderedDict() self._defaults = OrderedDict() - + self._file_or_files = file_or_files self._read_only = read_only self._is_initialized = False self._lock = None - + if not read_only: if isinstance(file_or_files, (tuple, list)): raise ValueError("Write-ConfigParsers can operate on a single file only, multiple files have been passed") # END single file check - + if not isinstance(file_or_files, basestring): file_or_files = file_or_files.name # END get filename from handle/stream # initialize lock base - we want to write self._lock = self.t_lock(file_or_files) - + self._lock._obtain_lock() # END read-only check - - + + def __del__(self): """Write pending changes if required and release locks""" # checking for the lock here makes sure we do not raise during write() # in case an invalid parser was created who could not get a lock if self.read_only or not self._lock._has_lock(): return - + try: try: self.write() @@ -187,20 +187,20 @@ class GitConfigParser(cp.RawConfigParser, object): print "Exception during destruction of GitConfigParser: %s" % str(e) finally: self._lock._release_lock() - + def optionxform(self, optionstr): """Do not transform options in any way when writing""" return optionstr - + def _read(self, fp, fpname): """A direct copy of the py2.4 version of the super class's _read method to assure it uses ordered dicts. Had to change one line to make it work. - + Future versions have this fixed, but in fact its quite embarassing for the guys not to have done it right in the first place ! - + Removed big comments to make it more compact. - + Made sure it ignores initial whitespace as git uses tabs""" cursect = None # None, or a dictionary optname = None @@ -260,21 +260,21 @@ class GitConfigParser(cp.RawConfigParser, object): # if any parsing errors occurred, raise an exception if e: raise e - - + + def read(self): """Reads the data stored in the files we have been initialized with. It will ignore files that cannot be read, possibly leaving an empty configuration - + :return: Nothing :raise IOError: if a file cannot be handled""" if self._is_initialized: return - + files_to_read = self._file_or_files if not isinstance(files_to_read, (tuple, list)): files_to_read = [ files_to_read ] - + for file_object in files_to_read: fp = file_object close_fp = False @@ -286,7 +286,7 @@ class GitConfigParser(cp.RawConfigParser, object): except IOError,e: continue # END fp handling - + try: self._read(fp, fp.name) finally: @@ -295,7 +295,7 @@ class GitConfigParser(cp.RawConfigParser, object): # END read-handling # END for each file object to read self._is_initialized = True - + def _write(self, fp): """Write an .ini-format representation of the configuration state in git compatible format""" @@ -306,28 +306,28 @@ class GitConfigParser(cp.RawConfigParser, object): fp.write("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) # END if key is not __name__ # END section writing - + if self._defaults: write_section(cp.DEFAULTSECT, self._defaults) map(lambda t: write_section(t[0],t[1]), self._sections.items()) - + @needs_values def write(self): """Write changes to our file, if there are changes at all - + :raise IOError: if this is a read-only writer instance or if we could not obtain a file lock""" self._assure_writable("write") - + fp = self._file_or_files close_fp = False - + # we have a physical file on disk, so get a lock if isinstance(fp, (basestring, file)): self._lock._obtain_lock() # END get lock for physical files - + if not hasattr(fp, "seek"): fp = open(self._file_or_files, "w") close_fp = True @@ -338,7 +338,7 @@ class GitConfigParser(cp.RawConfigParser, object): fp.truncate() #END # END handle stream or file - + # WRITE DATA try: self._write(fp) @@ -346,33 +346,33 @@ class GitConfigParser(cp.RawConfigParser, object): if close_fp: fp.close() # END data writing - + # we do not release the lock - it will be done automatically once the # instance vanishes - + def _assure_writable(self, method_name): if self.read_only: raise IOError("Cannot execute non-constant method %s.%s" % (self, method_name)) - + @needs_values @set_dirty_and_flush_changes def add_section(self, section): """Assures added options will stay in order""" super(GitConfigParser, self).add_section(section) self._sections[section] = OrderedDict() - + @property def read_only(self): """:return: True if this instance may change the configuration file""" return self._read_only - + def get_value(self, section, option, default = None): """ :param default: If not None, the given default value will be returned in case the option did not exist :return: a properly typed value, either int, float or string - + :raise TypeError: in case the value could not be understood Otherwise the exceptions known to the ConfigParser will be raised.""" try: @@ -381,7 +381,7 @@ class GitConfigParser(cp.RawConfigParser, object): if default is not None: return default raise - + types = ( long, float ) for numtype in types: try: @@ -395,29 +395,29 @@ class GitConfigParser(cp.RawConfigParser, object): except (ValueError,TypeError): continue # END for each numeric type - + # try boolean values as git uses them vl = valuestr.lower() if vl == 'false': return False if vl == 'true': return True - + if not isinstance( valuestr, basestring ): raise TypeError( "Invalid value type: only int, long, float and str are allowed", valuestr ) - + return valuestr - + @needs_values @set_dirty_and_flush_changes def set_value(self, section, option, value): """Sets the given option in section to the given value. It will create the section if required, and will not throw as opposed to the default ConfigParser 'set' method. - + :param section: Name of the section in which the option resides or should reside :param option: Name of the options whose value to set - + :param value: Value to set the option to. It must be a string or convertible to a string""" if not self.has_section(section): |