diff --git a/rhodecode/lib/vcs/backends/git/config.py b/rhodecode/lib/vcs/backends/git/config.py deleted file mode 100644 --- a/rhodecode/lib/vcs/backends/git/config.py +++ /dev/null @@ -1,345 +0,0 @@ -# config.py - Reading and writing Git config files -# Copyright (C) 2011 Jelmer Vernooij -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. - -"""Reading and writing Git configuration files. - -TODO: - * preserve formatting when updating configuration files - * treat subsection names as case-insensitive for [branch.foo] style - subsections -""" - -# Taken from dulwich not yet released 0.8.3 version (until it is actually -# released) - -import errno -import os -import re - -from dulwich.file import GitFile - - -class Config(object): - """A Git configuration.""" - - def get(self, section, name): - """Retrieve the contents of a configuration setting. - - :param section: Tuple with section name and optional subsection namee - :param subsection: Subsection name - :return: Contents of the setting - :raise KeyError: if the value is not set - """ - raise NotImplementedError(self.get) - - def get_boolean(self, section, name, default=None): - """Retrieve a configuration setting as boolean. - - :param section: Tuple with section name and optional subsection namee - :param name: Name of the setting, including section and possible - subsection. - :return: Contents of the setting - :raise KeyError: if the value is not set - """ - try: - value = self.get(section, name) - except KeyError: - return default - if value.lower() == "true": - return True - elif value.lower() == "false": - return False - raise ValueError("not a valid boolean string: %r" % value) - - def set(self, section, name, value): - """Set a configuration value. - - :param name: Name of the configuration value, including section - and optional subsection - :param: Value of the setting - """ - raise NotImplementedError(self.set) - - -class ConfigDict(Config): - """Git configuration stored in a dictionary.""" - - def __init__(self, values=None): - """Create a new ConfigDict.""" - if values is None: - values = {} - self._values = values - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._values) - - def __eq__(self, other): - return ( - isinstance(other, self.__class__) and - other._values == self._values) - - @classmethod - def _parse_setting(cls, name): - parts = name.split(".") - if len(parts) == 3: - return (parts[0], parts[1], parts[2]) - else: - return (parts[0], None, parts[1]) - - def get(self, section, name): - if isinstance(section, basestring): - section = (section, ) - if len(section) > 1: - try: - return self._values[section][name] - except KeyError: - pass - return self._values[(section[0],)][name] - - def set(self, section, name, value): - if isinstance(section, basestring): - section = (section, ) - self._values.setdefault(section, {})[name] = value - - -def _format_string(value): - if (value.startswith(" ") or - value.startswith("\t") or - value.endswith(" ") or - value.endswith("\t")): - return '"%s"' % _escape_value(value) - return _escape_value(value) - - -def _parse_string(value): - value = value.strip() - ret = [] - block = [] - in_quotes = False - for c in value: - if c == "\"": - in_quotes = (not in_quotes) - ret.append(_unescape_value("".join(block))) - block = [] - elif c in ("#", ";") and not in_quotes: - # the rest of the line is a comment - break - else: - block.append(c) - - if in_quotes: - raise ValueError("value starts with quote but lacks end quote") - - ret.append(_unescape_value("".join(block)).rstrip()) - - return "".join(ret) - - -def _unescape_value(value): - """Unescape a value.""" - def unescape(c): - return { - "\\\\": "\\", - "\\\"": "\"", - "\\n": "\n", - "\\t": "\t", - "\\b": "\b", - }[c.group(0)] - return re.sub(r"(\\.)", unescape, value) - - -def _escape_value(value): - """Escape a value.""" - return value.replace("\\", "\\\\").replace("\n", "\\n")\ - .replace("\t", "\\t").replace("\"", "\\\"") - - -def _check_variable_name(name): - for c in name: - if not c.isalnum() and c != '-': - return False - return True - - -def _check_section_name(name): - for c in name: - if not c.isalnum() and c not in ('-', '.'): - return False - return True - - -def _strip_comments(line): - line = line.split("#")[0] - line = line.split(";")[0] - return line - - -class ConfigFile(ConfigDict): - """A Git configuration file, like .git/config or ~/.gitconfig. - """ - - @classmethod - def from_file(cls, f): - """Read configuration from a file-like object.""" - ret = cls() - section = None - setting = None - for lineno, line in enumerate(f.readlines()): - line = line.lstrip() - if setting is None: - if _strip_comments(line).strip() == "": - continue - if line[0] == "[": - line = _strip_comments(line).rstrip() - if line[-1] != "]": - raise ValueError("expected trailing ]") - key = line.strip() - pts = key[1:-1].split(" ", 1) - pts[0] = pts[0].lower() - if len(pts) == 2: - if pts[1][0] != "\"" or pts[1][-1] != "\"": - raise ValueError( - "Invalid subsection " + pts[1]) - else: - pts[1] = pts[1][1:-1] - if not _check_section_name(pts[0]): - raise ValueError("invalid section name %s" % - pts[0]) - section = (pts[0], pts[1]) - else: - if not _check_section_name(pts[0]): - raise ValueError("invalid section name %s" % - pts[0]) - pts = pts[0].split(".", 1) - if len(pts) == 2: - section = (pts[0], pts[1]) - else: - section = (pts[0], ) - ret._values[section] = {} - else: - if section is None: - raise ValueError("setting %r without section" % line) - try: - setting, value = line.split("=", 1) - except ValueError: - setting = line - value = "true" - setting = setting.strip().lower() - if not _check_variable_name(setting): - raise ValueError("invalid variable name %s" % setting) - if value.endswith("\\\n"): - value = value[:-2] - continuation = True - else: - continuation = False - value = _parse_string(value) - ret._values[section][setting] = value - if not continuation: - setting = None - else: # continuation line - if line.endswith("\\\n"): - line = line[:-2] - continuation = True - else: - continuation = False - value = _parse_string(line) - ret._values[section][setting] += value - if not continuation: - setting = None - return ret - - @classmethod - def from_path(cls, path): - """Read configuration from a file on disk.""" - f = GitFile(path, 'rb') - try: - ret = cls.from_file(f) - ret.path = path - return ret - finally: - f.close() - - def write_to_path(self, path=None): - """Write configuration to a file on disk.""" - if path is None: - path = self.path - f = GitFile(path, 'wb') - try: - self.write_to_file(f) - finally: - f.close() - - def write_to_file(self, f): - """Write configuration to a file-like object.""" - for section, values in self._values.iteritems(): - try: - section_name, subsection_name = section - except ValueError: - (section_name, ) = section - subsection_name = None - if subsection_name is None: - f.write("[%s]\n" % section_name) - else: - f.write("[%s \"%s\"]\n" % (section_name, subsection_name)) - for key, value in values.iteritems(): - f.write("%s = %s\n" % (key, _escape_value(value))) - - -class StackedConfig(Config): - """Configuration which reads from multiple config files..""" - - def __init__(self, backends, writable=None): - self.backends = backends - self.writable = writable - - def __repr__(self): - return "<%s for %r>" % (self.__class__.__name__, self.backends) - - @classmethod - def default_backends(cls): - """Retrieve the default configuration. - - This will look in the repository configuration (if for_path is - specified), the users' home directory and the system - configuration. - """ - paths = [os.path.expanduser("~/.gitconfig"), "/etc/gitconfig"] - backends = [] - for path in paths: - try: - cf = ConfigFile.from_path(path) - except (IOError, OSError), e: - if e.errno != errno.ENOENT: - raise - else: - continue - backends.append(cf) - return backends - - def get(self, section, name): - for backend in self.backends: - try: - return backend.get(section, name) - except KeyError: - pass - raise KeyError(name) - - def set(self, section, name, value): - if self.writable is None: - raise NotImplementedError(self.set) - return self.writable.set(section, name, value)