Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
# config.py - Reading and writing Git config files# Copyright (C) 2011 Jelmer Vernooij <jelmer@samba.org>## 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"""importerrnoimport os
import re
+from collections import OrderedDictfrom UserDict import DictMixin
from dulwich.file import GitFile
classConfig(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 """raiseNotImplementedError(self.get)defget_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)exceptKeyError:returndefaultifvalue.lower()=="true":returnTrueelifvalue.lower()=="false":returnFalseraiseValueError("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
"""raiseNotImplementedError(self.set)classConfigDict(Config,DictMixin):"""Git configuration stored in a dictionary.""" def __init__(self, values=None):
"""Create a new ConfigDict."""
if values is None:
- values = {}+ values = OrderedDict() self._values = values
def __repr__(self):
return"%s(%r)"%(self.__class__.__name__,self._values)def__eq__(self,other):return(isinstance(other,self.__class__)andother._values==self._values) def __getitem__(self, key):
return self._values[key]
-+ def __setitem__(self, key, value):
self._values[key] = value
-+ def keys(self):
return self._values.keys()
@classmethoddef_parse_setting(cls,name):parts=name.split(".")iflen(parts)==3:return(parts[0],parts[1],parts[2])else:return(parts[0],None,parts[1])defget(self,section,name):ifisinstance(section,basestring):section=(section,)iflen(section)>1:try:returnself._values[section][name]exceptKeyError:passreturnself._values[(section[0],)][name] def set(self, section, name, value):
if isinstance(section, basestring):
section = (section, )
- self._values.setdefault(section, {})[name] = value
+ self._values.setdefault(section, OrderedDict())[name] = value
def _format_string(value):
if(value.startswith(" ")orvalue.startswith("\t")orvalue.endswith(" ")orvalue.endswith("\t")):return'"%s"'%_escape_value(value)return_escape_value(value)def_parse_string(value):value=value.strip()ret=[]block=[]in_quotes=Falseforcinvalue:ifc=="\"":in_quotes=(notin_quotes)ret.append(_unescape_value("".join(block)))block=[]elifcin("#",";")andnotin_quotes:# the rest of the line is a commentbreakelse:block.append(c)ifin_quotes:raiseValueError("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."""defunescape(c):return{"\\\\":"\\","\\\"":"\"","\\n":"\n","\\t":"\t","\\b":"\b",}[c.group(0)]returnre.sub(r"(\\.)",unescape,value)def_escape_value(value):"""Escape a value."""returnvalue.replace("\\","\\\\").replace("\n","\\n").replace("\t","\\t").replace("\"","\\\"")def_check_variable_name(name):forcinname:ifnotc.isalnum()andc!='-':returnFalsereturnTruedef_check_section_name(name):forcinname:ifnotc.isalnum()andcnotin('-','.'):returnFalsereturnTruedef_strip_comments(line):line=line.split("#")[0]line=line.split(";")[0]returnlineclassConfigFile(ConfigDict):"""A Git configuration file, like .git/config or ~/.gitconfig. """@classmethoddeffrom_file(cls,f):"""Read configuration from a file-like object."""ret=cls()section=Nonesetting=Noneforlineno,lineinenumerate(f.readlines()):line=line.lstrip()ifsettingisNone:iflen(line)>0andline[0]=="[":line=_strip_comments(line).rstrip()last=line.index("]")iflast==-1:raiseValueError("expected trailing ]")pts=line[1:last].split(" ",1)line=line[last+1:]pts[0]=pts[0].lower()iflen(pts)==2:ifpts[1][0]!="\""orpts[1][-1]!="\"":raiseValueError("Invalid subsection "+pts[1])else:pts[1]=pts[1][1:-1]ifnot_check_section_name(pts[0]):raiseValueError("invalid section name %s"%pts[0])section=(pts[0],pts[1])else:ifnot_check_section_name(pts[0]):raiseValueError("invalid section name %s"%pts[0])pts=pts[0].split(".",1)iflen(pts)==2: section = (pts[0], pts[1])
else:
section = (pts[0], )
- ret._values[section] = {}+ ret._values[section] = OrderedDict() if _strip_comments(line).strip() == "":
continue
if section is None:
raiseValueError("setting %r without section"%line)try:setting,value=line.split("=",1)exceptValueError:setting=linevalue="true"setting=setting.strip().lower()ifnot_check_variable_name(setting):raiseValueError("invalid variable name %s"%setting)ifvalue.endswith("\\\n"):value=value[:-2]continuation=Trueelse:continuation=Falsevalue=_parse_string(value)ret._values[section][setting]=valueifnotcontinuation:setting=Noneelse:# continuation lineifline.endswith("\\\n"):line=line[:-2]continuation=Trueelse:continuation=Falsevalue=_parse_string(line)ret._values[section][setting]+=valueifnotcontinuation:setting=Nonereturnret@classmethoddeffrom_path(cls,path):"""Read configuration from a file on disk."""f=GitFile(path,'rb')try:ret=cls.from_file(f)ret.path=pathreturnretfinally:f.close()defwrite_to_path(self,path=None):"""Write configuration to a file on disk."""ifpathisNone:path=self.pathf=GitFile(path,'wb')try:self.write_to_file(f)finally:f.close()defwrite_to_file(self,f):"""Write configuration to a file-like object."""forsection,valuesinself._values.iteritems():try:section_name,subsection_name=sectionexceptValueError:(section_name,)=sectionsubsection_name=Noneifsubsection_nameisNone:f.write("[%s]\n"%section_name)else:f.write("[%s\"%s\"]\n"%(section_name,subsection_name))forkey,valueinvalues.iteritems():f.write("%s = %s\n"%(key,_escape_value(value)))classStackedConfig(Config):"""Configuration which reads from multiple config files.."""def__init__(self,backends,writable=None):self.backends=backendsself.writable=writabledef__repr__(self):return"<%s for %r>"%(self.__class__.__name__,self.backends)@classmethoddefdefault_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=[]paths.append(os.path.expanduser("~/.gitconfig"))paths.append("/etc/gitconfig")backends=[]forpathinpaths:try:cf=ConfigFile.from_path(path)except(IOError,OSError),e:ife.errno!=errno.ENOENT:raiseelse:continuebackends.append(cf)returnbackendsdefget(self,section,name):forbackendinself.backends:try:returnbackend.get(section,name)exceptKeyError:passraiseKeyError(name)defset(self,section,name,value):ifself.writableisNone:raiseNotImplementedError(self.set)returnself.writable.set(section,name,value)
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.