From f6aa8d116eb33293c0a9d6d600eb7c32832758b9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 Jan 2015 19:14:33 +0100 Subject: initial set of adjustments to make (most) imports work. More to come, especially when it's about strings --- git/config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 6a85760c..685dbed8 100644 --- a/git/config.py +++ b/git/config.py @@ -7,7 +7,11 @@ configuration files""" import re -import ConfigParser as cp +try: + import ConfigParser as cp +except ImportError: + # PY3 + import configparser as cp import inspect import logging -- cgit v1.2.1 From ae2ff0f9d704dc776a1934f72a339da206a9fff4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 Jan 2015 19:50:28 +0100 Subject: Dum brute force conversion of all types. However, StringIO really is ByteIO in most cases, and py2.7 should run but doesn't. This should be made work first. --- git/config.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 685dbed8..34fe290b 100644 --- a/git/config.py +++ b/git/config.py @@ -17,6 +17,10 @@ import logging from git.odict import OrderedDict from git.util import LockFile +from git.compat import ( + string_types, + FileType +) __all__ = ('GitConfigParser', 'SectionConstraint') @@ -175,7 +179,7 @@ class GitConfigParser(cp.RawConfigParser, object): "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): + if not isinstance(file_or_files, string_types): file_or_files = file_or_files.name # END get filename from handle/stream # initialize lock base - we want to write @@ -333,7 +337,7 @@ class GitConfigParser(cp.RawConfigParser, object): close_fp = False # we have a physical file on disk, so get a lock - if isinstance(fp, (basestring, file)): + if isinstance(fp, string_types + (FileType, )): self._lock._obtain_lock() # END get lock for physical files @@ -391,7 +395,7 @@ class GitConfigParser(cp.RawConfigParser, object): return default raise - types = (long, float) + types = (int, float) for numtype in types: try: val = numtype(valuestr) @@ -412,7 +416,7 @@ class GitConfigParser(cp.RawConfigParser, object): if vl == 'true': return True - if not isinstance(valuestr, basestring): + if not isinstance(valuestr, string_types): raise TypeError("Invalid value type: only int, long, float and str are allowed", valuestr) return valuestr -- cgit v1.2.1 From 04357d0d46fee938a618b64daed1716606e05ca5 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 15:53:46 +0100 Subject: Intermediate commit: test_config and test_actor works Kind of tackling the tasks step by step, picking low-hanging fruit first, or the ones that everyone depends on --- git/config.py | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 34fe290b..988547a0 100644 --- a/git/config.py +++ b/git/config.py @@ -14,12 +14,15 @@ except ImportError: import configparser as cp import inspect import logging +import abc from git.odict import OrderedDict from git.util import LockFile from git.compat import ( string_types, - FileType + FileType, + defenc, + with_metaclass ) __all__ = ('GitConfigParser', 'SectionConstraint') @@ -28,7 +31,7 @@ __all__ = ('GitConfigParser', 'SectionConstraint') log = logging.getLogger('git.config') -class MetaParserBuilder(type): +class MetaParserBuilder(abc.ABCMeta): """Utlity class wrapping base-class methods into decorators that assure read-only properties""" def __new__(metacls, name, bases, clsdict): @@ -39,7 +42,7 @@ class MetaParserBuilder(type): if kmm in clsdict: mutating_methods = clsdict[kmm] for base in bases: - methods = (t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_")) + methods = (t for t in inspect.getmembers(base, inspect.isroutine) if not t[0].startswith("_")) for name, method in methods: if name in clsdict: continue @@ -112,7 +115,7 @@ class SectionConstraint(object): return self._config -class GitConfigParser(cp.RawConfigParser, object): +class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): """Implements specifics required to read git style configuration files. @@ -128,7 +131,6 @@ class GitConfigParser(cp.RawConfigParser, object): :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. @@ -150,7 +152,6 @@ class GitConfigParser(cp.RawConfigParser, object): # 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 @@ -162,12 +163,12 @@ class GitConfigParser(cp.RawConfigParser, object): :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.""" - super(GitConfigParser, self).__init__() - # initialize base with ordered dictionaries to be sure we write the same - # file back - self._sections = OrderedDict() - self._defaults = OrderedDict() + cp.RawConfigParser.__init__(self, dict_type=OrderedDict) + # Used in python 3, needs to stay in sync with sections for underlying implementation to work + if not hasattr(self, '_proxies'): + self._proxies = self._dict() + self._file_or_files = file_or_files self._read_only = read_only self._is_initialized = False @@ -222,7 +223,8 @@ class GitConfigParser(cp.RawConfigParser, object): lineno = 0 e = None # None, or an exception while True: - line = fp.readline() + # we assume to read binary ! + line = fp.readline().decode(defenc) if not line: break lineno = lineno + 1 @@ -242,9 +244,9 @@ class GitConfigParser(cp.RawConfigParser, object): elif sectname == cp.DEFAULTSECT: cursect = self._defaults else: - # THE ONLY LINE WE CHANGED ! - cursect = OrderedDict((('__name__', sectname),)) + cursect = self._dict((('__name__', sectname),)) self._sections[sectname] = cursect + self._proxies[sectname] = None # So sections can't start with a continuation line optname = None # no section header in the file? @@ -295,7 +297,7 @@ class GitConfigParser(cp.RawConfigParser, object): # assume a path if it is not a file-object if not hasattr(file_object, "seek"): try: - fp = open(file_object) + fp = open(file_object, 'rb') close_fp = True except IOError: continue @@ -314,16 +316,17 @@ class GitConfigParser(cp.RawConfigParser, object): """Write an .ini-format representation of the configuration state in git compatible format""" def write_section(name, section_dict): - fp.write("[%s]\n" % name) + fp.write(("[%s]\n" % name).encode(defenc)) for (key, value) in section_dict.items(): if key != "__name__": - fp.write("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) + fp.write(("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))).encode(defenc)) # 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()) + for name, value in self._sections.items(): + write_section(name, value) @needs_values def write(self): @@ -371,8 +374,7 @@ class GitConfigParser(cp.RawConfigParser, object): @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() + return super(GitConfigParser, self).add_section(section) @property def read_only(self): -- cgit v1.2.1 From e1060a2a8c90c0730c3541811df8f906dac510a7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 5 Jan 2015 17:59:22 +0100 Subject: test_commit works once again --- git/config.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 988547a0..7917bc5a 100644 --- a/git/config.py +++ b/git/config.py @@ -201,6 +201,11 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje self.write() except IOError: log.error("Exception during destruction of GitConfigParser", exc_info=True) + except ReferenceError: + # This happens in PY3 ... and usually means that some state cannot be written + # as the sections dict cannot be iterated + # Usually when shutting down the interpreter, don'y know how to fix this + pass finally: self._lock._release_lock() @@ -345,7 +350,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # END get lock for physical files if not hasattr(fp, "seek"): - fp = open(self._file_or_files, "w") + fp = open(self._file_or_files, "wb") close_fp = True else: fp.seek(0) -- cgit v1.2.1 From 45c1c99a22e95d730d3096c339d97181d314d6ca Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 10:43:25 +0100 Subject: test_index works --- git/config.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 7917bc5a..96991b84 100644 --- a/git/config.py +++ b/git/config.py @@ -99,6 +99,13 @@ class SectionConstraint(object): self._config = config self._section_name = section + def __del__(self): + # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! + # Apparently __del__ doesn't get call anymore if refcount becomes 0 + # Ridiculous ... . + self._config.__del__() + # del self._config + def __getattr__(self, attr): if attr in self._valid_attrs_: return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) @@ -193,7 +200,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje """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(): + if self.read_only or (self._lock and not self._lock._has_lock()): return try: -- cgit v1.2.1 From a05e49d2419d65c59c65adf5cd8c05f276550e1d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 14:05:30 +0100 Subject: test_repo works --- git/config.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 96991b84..c7e8b7ac 100644 --- a/git/config.py +++ b/git/config.py @@ -103,8 +103,7 @@ class SectionConstraint(object): # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! # Apparently __del__ doesn't get call anymore if refcount becomes 0 # Ridiculous ... . - self._config.__del__() - # del self._config + self._config.release() def __getattr__(self, attr): if attr in self._valid_attrs_: @@ -121,6 +120,10 @@ class SectionConstraint(object): """return: Configparser instance we constrain""" return self._config + def release(self): + """Equivalent to GitConfigParser.release(), which is called on our underlying parser instance""" + return self._config.release() + class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): @@ -198,6 +201,13 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje def __del__(self): """Write pending changes if required and release locks""" + # NOTE: only consistent in PY2 + self.release() + + def release(self): + """Flush changes and release the configuration write lock. This instance must not be used anymore afterwards. + In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called + deterministically anymore.""" # 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 (self._lock and not self._lock._has_lock()): -- cgit v1.2.1 From e0c65d6638698f4e3a9e726efca8c0bcf466cd62 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 6 Jan 2015 15:38:20 +0100 Subject: Make flake8 happy --- git/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index c7e8b7ac..eefab299 100644 --- a/git/config.py +++ b/git/config.py @@ -178,7 +178,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # Used in python 3, needs to stay in sync with sections for underlying implementation to work if not hasattr(self, '_proxies'): self._proxies = self._dict() - + self._file_or_files = file_or_files self._read_only = read_only self._is_initialized = False @@ -206,7 +206,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje def release(self): """Flush changes and release the configuration write lock. This instance must not be used anymore afterwards. - In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called + In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called deterministically anymore.""" # 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 -- cgit v1.2.1