summaryrefslogtreecommitdiff
path: root/git/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'git/config.py')
-rw-r--r--git/config.py126
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):