porting code to python3.x with os patch 80/284780/5
authorbiao716.wang <biao716.wang@samsung.com>
Fri, 25 Nov 2022 13:24:26 +0000 (22:24 +0900)
committerbiao716.wang <biao716.wang@samsung.com>
Mon, 28 Nov 2022 12:21:23 +0000 (21:21 +0900)
Change-Id: I3deda71d6bde7c3363800d79d2e9ae6998bdb9e7
Signed-off-by: biao716.wang <biao716.wang@samsung.com>
44 files changed:
debian/control
debian/rules
examples/gbp-add-patch
examples/zeitgeist-git.py
gbp/command_wrappers.py
gbp/deb/changelog.py
gbp/deb/format.py
gbp/format.py
gbp/git/__init__.py
gbp/git/fastimport.py
gbp/git/modifier.py
gbp/git/repository.py
gbp/patch_series.py
gbp/paths.py [new file with mode: 0755]
gbp/rpm/__init__.py
gbp/rpm/linkedlist.py
gbp/scripts/common/buildpackage.py
gbp/scripts/common/import_orig.py
gbp/scripts/common/pq.py
gbp/scripts/import_dscs.py
gbp/scripts/import_srpm.py
gbp/scripts/pq.py
gbp/scripts/pq_rpm.py
gbp/scripts/rpm_ch.py
gbp/scripts/supercommand.py
packaging/git-buildpackage.spec
setup.py
tests/01_test_help.py
tests/07_test_fastimport.py
tests/09_test_write_tree.py
tests/10_test_get_upstream_tree.py
tests/11_test_dch_main.py
tests/12_test_deb.py
tests/13_test_gbp_pq.py
tests/14_test_gbp_import_dscs.py
tests/15_test_DebianSource.py
tests/18_test_Config.py
tests/component/__init__.py
tests/component/rpm/test_buildpackage_rpm.py
tests/component/rpm/test_import_srpm.py
tests/component/rpm/test_pq_rpm.py
tests/test_Changelog.py
tests/test_GitModifier.py
tests/test_GitRepository.py

index afd3198..0ccbc20 100755 (executable)
@@ -24,7 +24,7 @@ Standards-Version: 3.9.4
 Vcs-Git: git://honk.sigxcpu.org/git/git-buildpackage.git
 Vcs-Browser: https://honk.sigxcpu.org/gitweb/?p=git-buildpackage.git
 Homepage: https://honk.sigxcpu.org/piki/projects/git-buildpackage/
-X-Python-Version: >= 2.6
+X-Python3-Version: >= 3.5
 
 
 Package: git-buildpackage-common
@@ -32,14 +32,16 @@ Architecture: all
 Depends: ${python3:Depends},
  ${shlibs:Depends},
  ${misc:Depends},
+ devscripts (>= 2.13.5~),
  git (>= 1:1.7.9.1-1~),
  man-db,
+ python3-dateutil,
  python3-pkg-resources,
  python3-dateutil,
 #unittest need
  zipmerge
 Recommends: pristine-tar (>= 0.5)
-Suggests: python-notify, unzip, zipmerge
+Suggests: python3-notify2, unzip, zipmerge
 Description: Suite to help with packaging in Git repositories
  This package contains the common API and scripts for Debian and rpm packaging
 
index 91265d4..0ac72e4 100755 (executable)
@@ -37,8 +37,7 @@ endif
 override_dh_auto_build:
        dh_auto_build
        make -C docs
-       #generate apidocs
-       #sh gen_apidocs.sh
+
 override_dh_auto_install:
        dh_auto_install
        dh_bash-completion
index 5da1b02..9594f19 100755 (executable)
@@ -34,7 +34,8 @@ commits debian/patches/0010-bla-fasel with this changelog message:
 
 import re
 import sys
-import os, os.path
+import os
+import os.path
 from gbp.command_wrappers import (Command)
 from gbp.config import (GbpOptionParserDebian, GbpOptionGroup)
 from gbp.errors import GbpError
@@ -85,7 +86,8 @@ def main(argv):
     try:
         repo = GitRepository(os.path.curdir)
     except GitRepositoryError:
-        print >>sys.stderr, "%s is not a git repository" % (os.path.abspath('.'))
+        print("%s is not a git repository" % os.path.abspath('.'),
+              file=sys.stderr)
         return 1
 
     try:
@@ -105,8 +107,8 @@ def main(argv):
     except GitRepositoryError:
         retval = 1
     except GbpError as err:
-        if len(err.__str__()):
-            print >>sys.stderr, err
+        if str(err):
+            print(err, file=sys.stderr)
         retval = 1
 
     return retval
index 578f504..89b5d3a 100755 (executable)
@@ -1,7 +1,7 @@
 #! /usr/bin/python3
 # vim: set fileencoding=utf-8 :
 #
-# (C) 2010 Guido Guenther <agx@sigxcpu.org>
+# (C) 2010 Guido G¨¹nther <agx@sigxcpu.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; either version 2 of the License, or
@@ -13,8 +13,8 @@
 #    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#    along with this program; if not, please see
+#    <http://www.gnu.org/licenses/>
 #
 # Simple Zeitgeist Git data source
 
@@ -48,14 +48,14 @@ else:
     try:
         CLIENT = ZeitgeistClient()
     except RuntimeError as e:
-        print("Unable to connect to Zeitgeist, won't send events. Reason: '%s'" %e)
+        print("Unable to connect to Zeitgeist, won't send events. Reason: '%s'" % e)
 
 
 def get_repo():
     """Get uri of remote repository and its name"""
     repo = None
     uri = subprocess.Popen(['git', 'config', '--get', 'remote.origin.url'],
-                             stdout=subprocess.PIPE).communicate()[0]
+                           stdout=subprocess.PIPE).communicate()[0]
 
     if uri:
         uri = uri.strip().decode(sys.getfilesystemencoding())
@@ -64,10 +64,10 @@ def get_repo():
         else:
             sep = ':'
         try:
-            repo = str(uri.rsplit(sep, 1)[1])
-        except IndexError: # no known separator
+            repo = unicode(uri.rsplit(sep, 1)[1])
+        except IndexError:  # no known separator
             repo = uri
-        repo = repo.rsplit('.git', 1)[0]
+        repo = repo.rsplit(u'.git', 1)[0]
     return repo, uri
 
 
@@ -78,27 +78,27 @@ def main(argv):
     #           * branch
     #           * log summary (git log -1 --format=%s HEAD)
     curdir = os.path.abspath(os.curdir).decode(sys.getfilesystemencoding())
-    uri = "file://%s" % curdir
+    uri = u"file://%s" % curdir
 
     repo, origin = get_repo()
     if not repo:
-        repo = str(curdir.rsplit('/', 1)[1])
+        repo = unicode(curdir.rsplit('/', 1)[1])
         origin = uri
 
     subject = Subject.new_for_values(
-                uri = uri,
-                interpretation = Interpretation.DOCUMENT.TEXT_DOCUMENT.PLAIN_TEXT_DOCUMENT.SOURCE_CODE.uri,
-                manifestation = Manifestation.FILE_DATA_OBJECT.uri,
-                text = repo,
-                origin = origin)
+        uri=uri,
+        interpretation=Interpretation.DOCUMENT.TEXT_DOCUMENT.PLAIN_TEXT_DOCUMENT.SOURCE_CODE.uri,
+        manifestation=Manifestation.FILE_DATA_OBJECT.uri,
+        text=repo,
+        origin=origin)
     event = Event.new_for_values(
-                timestamp = int(time.time() * 1000),
-                interpretation = interpretation,
-                manifestation = Manifestation.USER_ACTIVITY.uri,
-                actor = "application://gitg.desktop",
-                subjects = [subject])
+        timestamp=int(time.time() * 1000),
+        interpretation=interpretation,
+        manifestation=Manifestation.USER_ACTIVITY.uri,
+        actor="application://gitg.desktop",
+        subjects=[subject])
     CLIENT.insert_event(event)
 
+
 if __name__ == '__main__':
     main(sys.argv)
-
index 0d29af1..b4f2445 100644 (file)
@@ -43,7 +43,7 @@ class Command(object):
         self.run_error = "'%s' failed" % (" ".join([self.cmd] + self.args))
         self.shell = shell
         self.retcode = 1
-        self.stderr = ''
+        self.stderr = b''
         self.capture_stderr = capture_stderr
         self.cwd = cwd
         if extra_env is not None:
@@ -62,7 +62,7 @@ class Command(object):
             signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
         log.debug("%s %s %s" % (self.cmd, self.args, args))
-        self.stderr = ''
+        self.stderr = b''
         stderr_arg = subprocess.PIPE if self.capture_stderr else None
         cmd = [ self.cmd ] + self.args + args
         if self.shell:
index 9b3c03b..41aca94 100644 (file)
@@ -93,10 +93,10 @@ class ChangeLog(object):
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
-        (output, errors) = cmd.communicate(self._contents)
+        (output, errors) = cmd.communicate(self._contents.encode('utf-8'))
         if cmd.returncode:
             raise ParseChangeLogError("Failed to parse changelog. "
-                                      "dpkg-parsechangelog said:\n%s" % (errors, ))
+                                      "dpkg-parsechangelog said:\n%s" % errors.decode().strip()))
         # Parse the result of dpkg-parsechangelog (which looks like
         # email headers)
         cp = email.message_from_string(output)
@@ -115,8 +115,8 @@ class ChangeLog(object):
         self._cp = cp
 
     def _read(self):
-            with open(self.filename) as f:
-                self._contents = f.read()
+        with open(self.filename, encoding='utf-8') as f:
+            self._contents = f.read()
 
     def __getitem__(self, item):
         return self._cp[item]
@@ -131,7 +131,7 @@ class ChangeLog(object):
 
     @property
     def name(self):
-        """The packges name"""
+        """The packages name"""
         return self._cp['Source']
 
     @property
index 3a4c8ab..cd56cdf 100644 (file)
@@ -84,7 +84,7 @@ class DebianSourceFormat(object):
 
         >>> import tempfile, os
         >>> with tempfile.NamedTemporaryFile(delete=False) as t:
-        ...    t.write("3.0 (quilt)")
+        ...    ret = t.write(b"3.0 (quilt)")
         >>> d = DebianSourceFormat.parse_file(t.name)
         >>> d.version
         '3.0'
index 2a4af15..59c932d 100644 (file)
@@ -42,3 +42,23 @@ def format_msg(msg, args):
             raise GbpError("Failed to format %s: Missing value %s in %s" % (msg, e, args))
 
 
+def format_b(fmtstr, *args):
+    """String-like interpolation for bytes objects.
+
+    NOTE: This is a compatibility wrapper for older versions (<3.5) of Python 3
+    which do not support the percent operator ('%') for bytes objects. This
+    function should be removed (and replaced by simple '%') when Python 3.5
+    has gained wide enough adoption.
+
+    >>> format_b(b'%s %d', b'foo', 123)
+    b'foo 123'
+    >>> format_b(b'foo 123')
+    b'foo 123'
+    >>> format_b('%s %d', b'foo', 123)
+    Traceback (most recent call last):
+    ...
+    AttributeError: 'str' object has no attribute 'decode'
+    """
+    fmtstr = fmtstr.decode()
+    strargs = tuple([(a.decode() if isinstance(a, bytes) else a) for a in args])
+    return (fmtstr % strargs).encode()
\ No newline at end of file
index 57b74ef..53c2bfc 100644 (file)
@@ -43,3 +43,4 @@ def rfc822_date_to_git(rfc822_date):
     return '%d %s' % (seconds, tz)
 
 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
+
index 435d5e0..9b27a2b 100644 (file)
@@ -20,6 +20,9 @@
 import subprocess
 import time
 from gbp.errors import GbpError
+from gbp.format import format_b
+from gbp.paths import to_bin
+
 
 class FastImport(object):
     """Add data to a git repository using I{git fast-import}"""
@@ -36,7 +39,7 @@ class FastImport(object):
         """
         self._repo = repo
         try:
-            self._fi = subprocess.Popen([ 'git', 'fast-import', '--quiet'],
+            self._fi = subprocess.Popen(['git', 'fast-import', '--quiet'],
                                         stdin=subprocess.PIPE, cwd=repo.path)
             self._out = self._fi.stdin
         except OSError as err:
@@ -46,17 +49,17 @@ class FastImport(object):
                 "Invalid argument when spawning git fast-import: %s" % err)
 
     def _do_data(self, fd, size):
-        self._out.write("data %s\n" % size)
+        self._out.write(format_b(b"data %d\n", size))
         while True:
             data = fd.read(self._bufsize)
             self._out.write(data)
             if len(data) != self._bufsize:
                 break
-        self._out.write("\n")
+        self._out.write(b"\n")
 
     def _do_file(self, filename, mode, fd, size):
-        name = "/".join(filename.split('/')[1:])
-        self._out.write("M %d inline %s\n" % (mode, name))
+        name = b"/".join(to_bin(filename).split(b'/')[1:])
+        self._out.write(format_b(b"M %d inline %s\n", mode, name))
         self._do_data(fd, size)
 
     def add_file(self, filename, fd, size, mode=m_regular):
@@ -83,9 +86,11 @@ class FastImport(object):
         @param linktarget: the target the symlink points to
         @type linktarget: C{str}
         """
-        self._out.write("M %d inline %s\n" % (self.m_symlink, linkname))
-        self._out.write("data %s\n" % len(linktarget))
-        self._out.write("%s\n" % linktarget)
+        linktarget = to_bin(linktarget)
+        linkname = to_bin(linkname)
+        self._out.write(format_b(b"M %d inline %s\n", self.m_symlink, linkname))
+        self._out.write(format_b(b"data %d\n", len(linktarget)))
+        self._out.write(format_b(b"%s\n", linktarget))
 
     def start_commit(self, branch, committer, msg):
         """
@@ -108,24 +113,23 @@ class FastImport(object):
         else:
             from_ = ''
 
-        self._out.write("""commit refs/heads/%(branch)s
+        s = """commit refs/heads/%(branch)s
 committer %(name)s <%(email)s> %(time)s
 data %(length)s
-%(msg)s%(from)s""" %
-            { 'branch': branch,
-              'name':   committer.name,
-              'email':  committer.email,
-              'time':   committer.date,
-              'length': length,
-              'msg': msg,
-              'from': from_,
-              })
+%(msg)s%(from)s""" % {'branch': branch,
+                      'name': committer.name,
+                      'email': committer.email,
+                      'time': committer.date,
+                      'length': length,
+                      'msg': msg,
+                      'from': from_}
+        self._out.write(s.encode())
 
     def deleteall(self):
         """
         Issue I{deleteall} to fastimport so we start from a empty tree
         """
-        self._out.write("deleteall\n")
+        self._out.write(b"deleteall\n")
 
     def close(self):
         """
index 5f5b599..ba5ad76 100644 (file)
@@ -20,7 +20,8 @@ Someone who modifiers something in git
 like committing changes or authoring a patch
 """
 
-import calendar, datetime
+import calendar
+import datetime
 
 from gbp.git.errors import GitError
 
@@ -72,7 +73,7 @@ class GitModifier(object):
                 self._date = date
             else:
                 self._date = date.replace(tzinfo=tz)
-        elif date != None:
+        elif date is not None:
             raise ValueError("Date '%s' not timestamp, "
                              "datetime object or git raw date" % date)
 
@@ -119,9 +120,11 @@ class GitModifier(object):
         """
         Get env vars for authorship information
 
-        >>> g = GitModifier("foo", "bar")
-        >>> g.get_author_env()
-        {'GIT_AUTHOR_EMAIL': 'bar', 'GIT_AUTHOR_NAME': 'foo'}
+        >>> g = GitModifier("Joey Ramone", "joey@example.com")
+        >>> g.get_author_env()['GIT_AUTHOR_EMAIL']
+        'joey@example.com'
+        >>> g.get_author_env()['GIT_AUTHOR_NAME']
+        'Joey Ramone'
 
         @return: Author information suitable to use as environment variables
         @rtype: C{dict}
@@ -132,15 +135,23 @@ class GitModifier(object):
         """
         Get env vars for committer information
 
-        >>> g = GitModifier("foo", "bar")
-        >>> g.get_committer_env()
-        {'GIT_COMMITTER_NAME': 'foo', 'GIT_COMMITTER_EMAIL': 'bar'}
+        >>> g = GitModifier("Joey Ramone", "joey@example.com")
+        >>> g.get_committer_env()['GIT_COMMITTER_EMAIL']
+        'joey@example.com'
+        >>> g.get_committer_env()['GIT_COMMITTER_NAME']
+        'Joey Ramone'
 
-        @return: Commiter information suitable to use as environment variables
+        @return: Committer information suitable to use as environment variables
         @rtype: C{dict}
         """
         return self._get_env('committer')
 
+    def get(self, key, default=None):
+        if key in self.keys():
+            return self.__getitem__(key)
+        else:
+            return default
+
     def __getitem__(self, key):
         if key == 'date':
             return self.date
index 4266167..3dfecdc 100644 (file)
 import subprocess
 import os.path
 import re
+import sys
 from collections import defaultdict
 import select
 
 import gbp.log as log
+from gbp.format import format_b
 from gbp.git.modifier import GitModifier
 from gbp.git.commit import GitCommit
 from gbp.git.errors import GitError
 from gbp.git.args import GitArgs
+from gbp.paths import to_bin
 
 
 class GitRepositoryError(GitError):
@@ -85,7 +88,7 @@ class GitRepository(object):
                 "Failed to get repository git dir at '%s'" % self.path)
 
         # Set git meta data dir
-        git_dir = out.strip()
+        git_dir = out.strip().decode(sys.getfilesystemencoding())
         if os.path.isabs(git_dir):
             self._git_dir = git_dir
         else:
@@ -106,6 +109,7 @@ class GitRepository(object):
             # Check for bare repository
             out, dummy, ret = self._git_inout('rev-parse', ['--is-bare-repository'],
                                               capture_stderr=True)
+            cdup = out.strip().decode(sys.getfilesystemencoding())
             if ret:
                 raise GitRepositoryError("No Git repository at '%s': '%s'" % (self.path, out))
             self._bare = False if out.strip() != 'true' else True
@@ -298,9 +302,8 @@ class GitRepository(object):
         except Exception as excobj:
             raise GitRepositoryError("Error running git %s: %s" % (command, excobj))
         if ret:
-            raise GitRepositoryError("Error running git %s: %s" %
-                                        (command, stderr.strip()))
-
+            detail = stderr or stdout
+            raise GitRepositoryError("Error running git %s: %s" % (command, detail.decode().strip()))
 
     def _cmd_has_feature(self, command, feature):
         """
@@ -315,12 +318,12 @@ class GitRepository(object):
         """
         args = GitArgs(command, '-m')
         help, stderr, ret = self._git_inout('help',
-                                           args.args,
-                                           extra_env={'LC_ALL': 'C'},
-                                           capture_stderr=True)
+                                            args.args,
+                                            extra_env={'LC_ALL': 'C'},
+                                            capture_stderr=True)
         if ret:
             raise GitRepositoryError("Invalid git command '%s': %s"
-                                     % (command, stderr[:-1]))
+                                     % (command, stderr.decode().strip()))
 
         # Parse git command man page
         section_re = re.compile(r'^(?P<section>[A-Z].*)')
@@ -328,7 +331,7 @@ class GitRepository(object):
         optopt_re = re.compile(r'--\[(?P<prefix>[a-zA-Z\-]+)\]-?')
         backspace_re = re.compile(".\b")
         man_section = None
-        for line in help.splitlines():
+        for line in help.decode().splitlines():
             if man_section == "OPTIONS" and line.startswith('       -'):
                 opts = line.split(',')
                 for opt in opts:
@@ -359,7 +362,7 @@ class GitRepository(object):
 
     @property
     def bare(self):
-        """Wheter this is a bare repository"""
+        """Whether this is a bare repository"""
         return self._bare
 
     @property
@@ -377,7 +380,7 @@ class GitRepository(object):
 
     @property
     def head(self):
-        """return the SHA1 of the current HEAD"""
+        """SHA1 of the current HEAD"""
         return self.rev_parse('HEAD')
 
 #{ Branches and Merging
@@ -433,13 +436,13 @@ class GitRepository(object):
         @raises GitRepositoryError: if HEAD is not a symbolic ref
           (e.g. when in detached HEAD state)
         """
-        out, dummy, ret = self._git_inout('symbolic-ref', [ 'HEAD' ],
-                                           capture_stderr=True)
+        out, _, ret = self._git_inout('symbolic-ref', ['HEAD'],
+                                      capture_stderr=True)
         if ret:
             # We don't append stderr since
             # "fatal: ref HEAD is not a symbolic ref" confuses people
             raise GitRepositoryError("Currently not on a branch")
-        ref = out.split('\n')[0]
+        ref = out.decode().split('\n')[0]
 
         # Check if ref really exists
         try:
@@ -517,9 +520,9 @@ class GitRepository(object):
         args.add(commit2)
         sha1, stderr, ret = self._git_inout('merge-base', args.args, capture_stderr=True)
         if not ret:
-            return self.strip_sha1(sha1)
+            return self.strip_sha1(sha1.decode())
         else:
-            raise GitRepositoryError("Failed to get common ancestor: %s" % stderr.strip())
+            raise GitRepositoryError("Failed to get common ancestor: %s" % stderr.decode().strip())
 
     def merge(self, commit, verbose=False, edit=False):
         """
@@ -529,7 +532,7 @@ class GitRepository(object):
         @type commit: C{str}
         @param verbose: whether to print a summary after the merge
         @type verbose: C{bool}
-        @param edit: wheter to invoke an editor to edit the merge message
+        @param edit: whether to invoke an editor to edit the merge message
         @type edit: C{bool}
         """
         args = GitArgs()
@@ -559,14 +562,15 @@ class GitRepository(object):
         """
         has_local = False       # local repo has new commits
         has_remote = False      # remote repo has new commits
-        out = self._git_getoutput('rev-list', ["--left-right",
+        out = self._git_getoutput('rev-list',
+                                  ["--left-right",
                                    "%s...%s" % (from_branch, to_branch),
                                    "--"])[0]
 
-        if not out: # both branches have the same commits
+        if not out:  # both branches have the same commits
             return True, True
 
-        for line in out:
+        for line in (l.decode() for l in out):
             if line.startswith("<"):
                 has_local = True
             elif line.startswith(">"):
@@ -588,10 +592,10 @@ class GitRepository(object):
         @return: local or remote branches
         @rtype: C{list}
         """
-        args = [ '--format=%(refname:short)' ]
-        args += [ 'refs/remotes/' ] if remote else [ 'refs/heads/' ]
+        args = ['--format=%(refname:short)']
+        args += ['refs/remotes/'] if remote else ['refs/heads/']
         out = self._git_getoutput('for-each-ref', args)[0]
-        return [ ref.strip() for ref in out ]
+        return [ref.decode().strip() for ref in out]
 
     def get_local_branches(self):
         """
@@ -649,8 +653,8 @@ class GitRepository(object):
         args.add('--contains')
         args.add(commit)
 
-        out, ret =  self._git_getoutput('branch', args.args)
-        for line in out:
+        out, ret = self._git_getoutput('branch', args.args)
+        for line in [l.decode() for l in out]:
             # remove prefix '*' for current branch before comparing
             line = line.replace('*', '')
             if line.strip() == branch:
@@ -703,7 +707,7 @@ class GitRepository(object):
 
         out = self._git_getoutput('for-each-ref', args.args)[0]
 
-        return out[0].strip()
+        return out[0].decode().strip()
 
 #{ Tags
 
@@ -802,9 +806,9 @@ class GitRepository(object):
         tag, err, ret = self._git_inout('describe', args.args,
                                         capture_stderr=True)
         if ret:
-            raise GitRepositoryError("Can't describe %s. Git error: %s" % \
-                                         (commitish, err.strip()))
-        return tag.strip()
+            raise GitRepositoryError("Can't describe %s. Git error: %s" %
+                                     (commitish, err.decode().strip()))
+        return tag.decode().strip()
 
     def find_tag(self, commit, pattern=None):
         """
@@ -828,8 +832,8 @@ class GitRepository(object):
         @return: tags
         @rtype: C{list} of C{str}
         """
-        args = [ '-l', pattern ] if pattern else []
-        return [ line.strip() for line in self._git_getoutput('tag', args)[0] ]
+        args = ['-l', pattern] if pattern else []
+        return [line.decode().strip() for line in self._git_getoutput('tag', args)[0]]
 
     def verify_tag(self, tag):
         """
@@ -902,7 +906,7 @@ class GitRepository(object):
             # Get a more helpful error message.
             out = self._status(porcelain=False,
                                 ignore_untracked=ignore_untracked)
-            return (False, "".join(out))
+            return (False, "".join([e.decode() for e in out]))
         else:
             return (True, '')
 
@@ -946,16 +950,16 @@ class GitRepository(object):
         if ret:
             raise GitRepositoryError("Can't get repository status: %s" % err)
 
-        elements = out.split('\x00')
+        elements = out.split(b'\x00')
         result = defaultdict(list)
 
-        while elements[0] != '':
+        while elements[0] != b'':
             element = elements.pop(0)
-            status = element[:2]
+            status = element[:2].decode()
             filepath = element[3:]
             # Expect to have two filenames for renames and copies
             if status[0] in ['R', 'C']:
-                filepath = elements.pop(0) + '\x00' + filepath
+                filepath = elements.pop(0) + b'\x00' + filepath
             result[status].append(filepath)
 
         return result
@@ -989,7 +993,7 @@ class GitRepository(object):
                                             capture_stderr=True)
         if ret:
             raise GitRepositoryError("revision '%s' not found" % name)
-        return self.strip_sha1(sha.splitlines()[0], short)
+        return self.strip_sha1(sha[0].decode(), short)
 
     @staticmethod
     def strip_sha1(sha1, length=0):
@@ -1002,7 +1006,7 @@ class GitRepository(object):
         >>> GitRepository.strip_sha1('58ef37d', 10)
         Traceback (most recent call last):
         ...
-        GitRepositoryError: '58ef37d' is not a valid sha1 of length 10
+        gbp.git.repository.GitRepositoryError: '58ef37d' is not a valid sha1 of length 10
         >>> GitRepository.strip_sha1('58ef37d', 7)
         '58ef37d'
         >>> GitRepository.strip_sha1('123456789', 7)
@@ -1010,16 +1014,16 @@ class GitRepository(object):
         >>> GitRepository.strip_sha1('foobar')
         Traceback (most recent call last):
         ...
-        GitRepositoryError: 'foobar' is not a valid sha1
+        gbp.git.repository.GitRepositoryError: 'foobar' is not a valid sha1
         """
         maxlen = 40
         s = sha1.strip()
 
-        l = length or maxlen
+        sl = length or maxlen
 
-        if len(s) < l or len(s) > maxlen:
+        if len(s) < sl or len(s) > maxlen:
             raise GitRepositoryError("'%s' is not a valid sha1%s" %
-                                     (s, " of length %d" % l if length else ""))
+                                     (s, " of length %d" % sl if length else ""))
         return s
 
 #{ Trees
@@ -1041,9 +1045,9 @@ class GitRepository(object):
         @return: C{True} if the repository has that tree, C{False} otherwise
         @rtype: C{bool}
         """
-        _out, _err, ret =  self._git_inout('ls-tree', [treeish],
-                                           capture_stderr=True)
-        return [ True, False ][ret != 0]
+        _out, _err, ret = self._git_inout('ls-tree', [treeish],
+                                          capture_stderr=True)
+        return [True, False][ret != 0]
 
     def write_tree(self, index_file=None):
         """
@@ -1063,28 +1067,30 @@ class GitRepository(object):
                                             extra_env=extra_env,
                                             capture_stderr=True)
         if ret:
-            raise GitRepositoryError("Can't write out current index: %s" % stderr[:-1])
-        return tree.strip()
+            raise GitRepositoryError("Can't write out current index: %s" % stderr.decode().strip())
+        return tree.decode().strip()
 
     def make_tree(self, contents):
         """
-        Create a tree based on contents. I{contents} has the same format than
-        the I{GitRepository.list_tree} output.
+        Create a tree based on contents.
+
+        @param contents: same format as I{GitRepository.list_tree} output.
+        @type contents: C{list} of C{str}
         """
-        out=''
+        objs = b''
         args = GitArgs('-z')
 
-        for obj in contents:
-             mode, type, sha1, name = obj
-             out += '%s %s %s\t%s\0' % (mode, type, sha1, name)
+        for mode, type_, sha1, name in contents:
+            name = to_bin(name)
+            objs += format_b(b'%s %s %s\t%s\0', mode.encode(), type_.encode(), sha1.encode(), name)
 
-        sha1, err, ret =  self._git_inout('mktree',
-                                          args.args,
-                                          out,
-                                          capture_stderr=True)
+        sha1, err, ret = self._git_inout('mktree',
+                                         args.args,
+                                         objs,
+                                         capture_stderr=True)
         if ret:
             raise GitRepositoryError("Failed to mktree: '%s'" % err)
-        return self.strip_sha1(sha1)
+        return self.strip_sha1(sha1.decode())
 
     def get_obj_type(self, obj):
         """
@@ -1098,12 +1104,12 @@ class GitRepository(object):
         out, ret = self._git_getoutput('cat-file', args=['-t', obj])
         if ret:
             raise GitRepositoryError("Not a Git repository object: '%s'" % obj)
-        return out[0].strip()
+        return out[0].decode().strip()
 
     def list_tree(self, treeish, recurse=False, paths=None):
         """
         Get a trees content. It returns a list of objects that match the
-        'ls-tree' output: [ mode, type, sha1, path ].
+        'ls-tree' output: [mode, type, sha1, path].
 
         @param treeish: the treeish object to list
         @type treeish: C{str}
@@ -1118,14 +1124,18 @@ class GitRepository(object):
         args.add("--")
         args.add_cond(paths, paths)
 
-        out, err, ret =  self._git_inout('ls-tree', args.args, capture_stderr=True)
+        out, err, ret = self._git_inout('ls-tree', args.args, capture_stderr=True)
         if ret:
-            raise GitRepositoryError("Failed to ls-tree '%s': '%s'" % (treeish, err))
+            raise GitRepositoryError("Failed to ls-tree '%s': '%s'" % (treeish, err.decode().strip()))
 
         tree = []
-        for line in out.split('\0'):
+        for line in out.split(b'\0'):
             if line:
-                tree.append(line.split(None, 3))
+                parts = line.split(None, 3)
+                # decode everything but the file name
+                for i in range(len(parts) - 1):
+                    parts[i] = parts[i].decode()
+                tree.append(parts)
         return tree
 
 #}
@@ -1139,8 +1149,9 @@ class GitRepository(object):
         @rtype: C{str}
         """
         value, ret = self._git_getoutput('config', [ name ])
-        if ret: raise KeyError
-        return value[0][:-1] # first line with \n ending removed
+        if ret:
+            raise KeyError("'%s' not found in git config")
+        return value[0].decode()[:-1]  # first line with \n ending removed
 
     def get_author_info(self):
         """
@@ -1151,11 +1162,11 @@ class GitRepository(object):
         @rtype: L{GitModifier}
         """
         try:
-           name =  self.get_config("user.name")
+            name =  self.get_config("user.name")
         except KeyError:
-           name = os.getenv("USER")
+            name = os.getenv("USER")
         try:
-           email =  self.get_config("user.email")
+            email = self.get_config("user.email")
         except KeyError:
             email = os.getenv("EMAIL")
         email = os.getenv("GIT_AUTHOR_EMAIL", email)
@@ -1173,19 +1184,19 @@ class GitRepository(object):
         """
         out, err, ret = self._git_inout('remote', [], capture_stderr=True)
         if ret:
-            raise GitRepositoryError('Failed to get list of remotes: %s' % err)
+            raise GitRepositoryError('Failed to get list of remotes: %s' % err.decode().strip())
 
         # Get information about all remotes
         remotes = {}
-        for remote in out.splitlines():
+        for remote in out.decode().splitlines():
             out, err, _ret = self._git_inout('remote', ['show', '-n', remote],
                                              capture_stderr=True)
             if ret:
                 raise GitRepositoryError('Failed to get information for remote '
-                                         '%s: %s' % (remote, err))
+                                         '%s: %s' % (remote, err.decode().strip()))
             fetch_url = None
             push_urls = []
-            for line in out.splitlines():
+            for line in out.decode().splitlines():
                 match = re.match('\s*Fetch\s+URL:\s*(\S.*)', line)
                 if match:
                     fetch_url = match.group(1)
@@ -1208,11 +1219,11 @@ class GitRepository(object):
         stdout, stderr, ret = self._git_inout('remote', ['-v'],
                                               capture_stderr=True)
         if ret:
-            raise GitRepositoryError('Failed to get remotes: %s' % stderr)
+            raise GitRepositoryError('Failed to get remotes: %s' % stderr.decode().strip())
 
         remotes = {}
         for rem in stdout.splitlines():
-            name, url_urltype = rem.split('\t', 1)
+            name, url_urltype = rem.remote.decode().strip().split('\t', 1)
             url, urltype = url_urltype.rsplit(' ', 1)
             urltype = urltype.strip('()')
             if not name in remotes:
@@ -1404,12 +1415,12 @@ class GitRepository(object):
 
         @param types: list of types to show
         @type types: C{list}
-        @return: list of files
+        @return: list of files as byte string
         @rtype: C{list} of C{str}
         """
-        all_types = [ 'cached', 'deleted', 'others', 'ignored',  'stage'
-                      'unmerged', 'killed', 'modified' ]
-        args = [ '-z' ]
+        all_types = ['cached', 'deleted', 'others', 'ignored', 'stage'
+                     'unmerged', 'killed', 'modified']
+        args = ['-z']
 
         for t in types:
             if t in all_types:
@@ -1420,7 +1431,7 @@ class GitRepository(object):
         if ret:
             raise GitRepositoryError("Error listing files: '%d'" % ret)
         if out:
-            return [ file for file in out[0].split('\0') if file ]
+            return [file for file in out[0].split(b'\0') if file]
         else:
             return []
 
@@ -1430,7 +1441,7 @@ class GitRepository(object):
         Hash a single file and write it into the object database
 
         @param filename: the filename to the content of the file to hash
-        @type filename: C{str}
+        @type filename: C{bytestr}
         @param filters: whether to run filters
         @type filters: C{bool}
         @return: the hash of the file
@@ -1444,10 +1455,9 @@ class GitRepository(object):
                                             args.args,
                                             capture_stderr=True)
         if not ret:
-            return self.strip_sha1(sha1)
+            return self.strip_sha1(sha1.decode())
         else:
-            raise GitRepositoryError("Failed to hash %s: %s" % (filename,
-                                                                stderr))
+            raise GitRepositoryError("Failed to hash %s: %s" % (filename, stderr.decode().strip()))
 #}
 
 #{ Comitting
@@ -1606,13 +1616,13 @@ class GitRepository(object):
             args += [ '-p' , parent ]
         sha1, stderr, ret = self._git_inout('commit-tree',
                                             args,
-                                            msg,
+                                            msg.encode(),
                                             extra_env,
                                             capture_stderr=True)
         if not ret:
-            return self.strip_sha1(sha1)
+            return self.strip_sha1(sha1.decode())
         else:
-            raise GitRepositoryError("Failed to commit tree: %s" % stderr)
+            raise GitRepositoryError("Failed to commit tree: %s" % stderr.decode().strip())
 
 #{ Commit Information
 
@@ -1645,22 +1655,26 @@ class GitRepository(object):
         args.add_cond(options, options)
         args.add("--")
         if isinstance(paths, str):
-            paths = [ paths ]
+            paths = [paths]
         args.add_cond(paths, paths)
 
         commits, ret = self._git_getoutput('log', args.args)
         if ret:
             where = " on %s" % paths if paths else ""
             raise GitRepositoryError("Error getting commits %s..%s%s" %
-                        (since, until, where))
-        return [ commit.strip() for commit in commits ]
+                                     (since, until, where))
+        return [commit.decode().strip() for commit in commits]
 
     def show(self, id):
-        """git-show id"""
+        """
+        Show a git object
+
+        @rtype: C{bytestr}
+        """
         obj, stderr, ret = self._git_inout('show', ["--pretty=medium", id],
-                                              capture_stderr=True)
+                                           capture_stderr=True)
         if ret:
-            raise GitRepositoryError("can't get %s: %s" % (id, stderr.rstrip()))
+            raise GitRepositoryError("can't get %s: %s" % (id, stderr.decode().rstrip()))
         return obj
 
     def grep_log(self, regex, since=None):
@@ -1682,9 +1696,9 @@ class GitRepository(object):
                                               capture_stderr=True)
         if ret:
             raise GitRepositoryError("Error grepping log for %s: %s" %
-                                     (regex, stderr[:-1]))
+                                     (regex, stderr.decode().strip()))
         if stdout:
-            return [ commit.strip() for commit in stdout.split('\n')[::-1] ]
+            return [commit.strip() for commit in stdout.decode().split('\n')[::-1]]
         else:
             return []
 
@@ -1713,36 +1727,38 @@ class GitRepository(object):
         args = GitArgs('--pretty=format:%an%x00%ae%x00%ad%x00%cn%x00%ce%x00%cd%x00%s%x00%f%x00%b%x00',
                        '-z', '--date=raw', '--no-renames', '--name-status',
                        commit_sha1)
-        out, err, ret =  self._git_inout('show', args.args)
+        out, err, ret = self._git_inout('show', args.args)
         if ret:
             raise GitRepositoryError("Unable to retrieve commit info for %s"
                                      % commitish)
 
-        fields = out.split('\x00')
+        fields = out.split(b'\x00')
 
-        author = GitModifier(fields[0].strip(),
-                             fields[1].strip(),
-                             fields[2].strip())
-        committer = GitModifier(fields[3].strip(),
-                                fields[4].strip(),
-                                fields[5].strip())
+        author = GitModifier(fields[0].decode().strip(),
+                             fields[1].decode().strip(),
+                             fields[2].decode().strip())
+        committer = GitModifier(fields[3].decode().strip(),
+                                fields[4].decode().strip(),
+                                fields[5].decode().strip())
 
         files = defaultdict(list)
         file_fields = fields[9:]
+
         # For some reason git returns one extra empty field for merge commits
-        if file_fields[0] == '': file_fields.pop(0)
-        while len(file_fields) and file_fields[0] != '':
-            status = file_fields.pop(0).strip()
+        if file_fields[0] == b'':
+            file_fields.pop(0)
+        while len(file_fields) and file_fields[0] != b'':
+            status = file_fields.pop(0).decode().strip()
             path = file_fields.pop(0)
             files[status].append(path)
 
-        return {'id' : commitish,
-                'author' : author,
-                'committer' : committer,
-                'subject' : fields[6],
-                'patchname' : fields[7],
-                'body' : fields[8],
-                'files' : files}
+        return {'id': commitish,
+                'author': author,
+                'committer': committer,
+                'subject': fields[6].decode(),
+                'patchname': fields[7].decode(),
+                'body': fields[8].decode(),
+                'files': files}
 
 #{ Patches
     def format_patches(self, start, end, output_dir,
@@ -1803,7 +1819,7 @@ class GitRepository(object):
         @param ignore_submodules: ignore changes to submodules
         @type ignore_submodules: C{bool}
         @return: diff
-        @rtype: C{str}
+        @rtype: C{binary}
         """
         options = GitArgs('-p', '--no-ext-diff')
         if stat is True:
@@ -1836,11 +1852,11 @@ class GitRepository(object):
         options = GitArgs('--name-status', '-z', obj1, obj2)
         output, stderr, ret = self._git_inout('diff', options.args)
 
-        elements = output.split('\x00')
+        elements = output.split(b'\x00')
         result = defaultdict(list)
 
-        while elements[0] != '':
-            status = elements.pop(0)[0]
+        while elements[0] != b'':
+            status = elements.pop(0).decode()[0]
             filepath = elements.pop(0)
             # Expect to have two filenames for renames and copies
             if status in ['R', 'C']:
@@ -1879,8 +1895,7 @@ class GitRepository(object):
         if output:
             out, err, ret = self._git_inout('archive', args.args)
             if ret:
-                raise GitRepositoryError("Unable to archive %s: %s" % (treeish,
-                                                                       err))
+                raise GitRepositoryError("Unable to archive %s: %s" % (treeish, err.decode().strip()))
         else:
             return self._git_inout2('archive', args.args)
 
@@ -1969,13 +1984,16 @@ class GitRepository(object):
             args += ['-r']
 
         out, ret =  self._git_getoutput('ls-tree', args, cwd=path)
-        for line in out:
-            mode, objtype, commit, name = line[:-1].split(None, 3)
+        for line in out.split(b'\n'):
+            if not line:
+                continue
+            mode, objtype, commit, name = line.decode().split(None, 3)
+
             # A submodules is shown as "commit" object in ls-tree:
             if objtype == "commit":
                 nextpath = os.path.join(path, name)
-                submodules.append( (nextpath.replace(self.path,'').lstrip('/'),
-                                    commit) )
+                submodules.append((nextpath.replace(self.path, '').lstrip('/'),
+                                   commit))
                 if recursive:
                     submodules += self.get_submodules(commit, path=nextpath,
                                                       recursive=recursive)
@@ -2004,7 +2022,7 @@ class GitRepository(object):
         try:
             if not os.path.exists(abspath):
                 os.makedirs(abspath)
-            stderr = ''
+            stderr = b''
             try:
                 for out in klass.__git_inout(command='init',
                                              args=args.args,
@@ -2015,7 +2033,7 @@ class GitRepository(object):
                                              capture_stdout=True):
                     stderr += out[1]
             except GitRepositoryError:
-                raise GitRepositoryError("Error running git init: %s" % stderr)
+                raise GitRepositoryError("Error running git init: %s" % stderr.decode().strip())
             except Exception as excobj:
                 raise GitRepositoryError("Error running git init: %s" % excobj)
             if description:
@@ -2025,7 +2043,7 @@ class GitRepository(object):
             return klass(abspath)
         except OSError as err:
             raise GitRepositoryError("Cannot create Git repository at '%s': %s"
-                                     % (abspath, err[1]))
+                                     % (abspath, err))
         return None
 
     @classmethod
@@ -2068,7 +2086,7 @@ class GitRepository(object):
         try:
             if not os.path.exists(abspath):
                 os.makedirs(abspath)
-            stderr = ''
+            stderr = b''
             try:
                 for out in klass.__git_inout(command='clone',
                                              args=args.args,
@@ -2079,7 +2097,7 @@ class GitRepository(object):
                                              capture_stdout=True):
                     stderr += out[1]
             except GitRepositoryError:
-                raise GitRepositoryError("Error running git clone: %s" % stderr)
+                raise GitRepositoryError("Error running git clone: %s" % stderr.decode())
             except Exception as excobj:
                 raise GitRepositoryError("Error running git clone: %s" % excobj)
 
index f608f20..caebfbf 100644 (file)
@@ -49,7 +49,7 @@ class Patch(object):
         repr = "<gbp.patch_series.Patch path='%s' " % self.path
         if self.topic:
             repr += "topic='%s' " % self.topic
-        if self.strip != None:
+        if self.strip is not None:
             repr += "strip=%d " % self.strip
         repr += ">"
         return repr
@@ -62,22 +62,23 @@ class Patch(object):
         """
         self.info = {}
         body = tempfile.NamedTemporaryFile(prefix='gbp_')
-        pipe = subprocess.Popen("git mailinfo '%s' /dev/null 2>/dev/null < '%s'" %
+        pipe = subprocess.Popen("git mailinfo -k '%s' /dev/null 2>/dev/null < '%s'" %
                                 (body.name, self.path),
                                 shell=True,
                                 stdout=subprocess.PIPE).stdout
         for line in pipe:
+            line = line.decode()
             if ':' in line:
                 rfc_header, value = line.split(" ", 1)
                 header = rfc_header[:-1].lower()
                 self.info[header] = value.strip()
         try:
-            self.long_desc = body.read()
-            body.close()
-        except IOError as msg:
+            self.long_desc = "".join([l.decode("utf-8", "backslashreplace") for l in body])
+        except (IOError, UnicodeDecodeError) as msg:
             raise GbpError("Failed to read patch header of '%s': %s" %
-                           (self.patch, msg))
+                           (self.path, msg))
         finally:
+            body.close()
             if os.path.exists(body.name):
                 os.unlink(body.name)
 
@@ -112,7 +113,7 @@ class Patch(object):
             if ext in self.patch_exts:
                 subject = base
         except ValueError:
-                pass # No ext so keep subject as is
+                pass  # No ext so keep subject as is
         return subject.lstrip('0123456789-') or subject
 
     def _get_info_field(self, key, get_val=None):
@@ -125,9 +126,9 @@ class Patch(object):
         @param key: key to fetch
         @type key: C{str}
         @param get_val: alternate value if key is not in info dict
-        @type get_val: C{str}
+        @type get_val: C{()->str}
         """
-        if self.info == None:
+        if self.info is None:
             self._read_info()
 
         if key in self.info:
@@ -205,11 +206,11 @@ class PatchSeries(list):
         queue = PatchSeries()
         for line in series:
             try:
-                if line[0] in [ '\n', '#' ]:
+                if line[0] in ['\n', '#']:
                     continue
             except IndexError:
-                continue # ignore empty lines
-            queue.append(klass._parse_line(line, patch_dir))
+                continue  # ignore empty lines
+            queue.append(cls._parse_line(line, patch_dir))
         return queue
 
     @staticmethod
@@ -223,7 +224,7 @@ class PatchSeries(list):
         >>> PatchSeries._get_topic("/asdf")
         """
         topic = os.path.dirname(line)
-        if topic in [ '', '/' ]:
+        if topic in ['', '/']:
             topic = None
         return topic
 
diff --git a/gbp/paths.py b/gbp/paths.py
new file mode 100755 (executable)
index 0000000..1cb7bcc
--- /dev/null
@@ -0,0 +1,26 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2017 Guido Günther <agx@sigxcpu.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; either version 2 of the License, or
+#    (at your option) any 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, please see
+#    <http://www.gnu.org/licenses/>
+"Helpers to handle paths"
+
+
+def to_bin(path):
+    """Convert to binary if not already
+
+    We want paths to be binary since we can't assume an encoding but
+    it shall still be convenient to pass in unicode strings
+    """
+    return path.encode() if not isinstance(path, bytes) else path
index 0a376e1..62f0c27 100644 (file)
@@ -38,6 +38,11 @@ from gbp.rpm.linkedlist import LinkedList
 from gbp.rpm.lib_rpm import librpm, get_librpm_log
 
 
+def _decode(s):
+    if s is not None:
+        return s.decode()
+
+
 class NoSpecError(Exception):
     """Spec file parsing error"""
     pass
@@ -72,8 +77,8 @@ class SrcRpmFile(object):
     @property
     def version(self):
         """Get the (downstream) version of the RPM package"""
-        version = dict(upstreamversion = self.rpmhdr[librpm.RPMTAG_VERSION],
-                       release = self.rpmhdr[librpm.RPMTAG_RELEASE])
+        version = dict(upstreamversion=self.rpmhdr[librpm.RPMTAG_VERSION].decode(),
+                       release=self.rpmhdr[librpm.RPMTAG_RELEASE].decode())
         if self.rpmhdr[librpm.RPMTAG_EPOCH] is not None:
             version['epoch'] = str(self.rpmhdr[librpm.RPMTAG_EPOCH])
         return version
@@ -81,17 +86,17 @@ class SrcRpmFile(object):
     @property
     def name(self):
         """Get the name of the RPM package"""
-        return self.rpmhdr[librpm.RPMTAG_NAME]
+        return self.rpmhdr[librpm.RPMTAG_NAME].decode()
 
     @property
     def upstreamversion(self):
         """Get the upstream version of the RPM package"""
-        return self.rpmhdr[librpm.RPMTAG_VERSION]
+        return self.rpmhdr[librpm.RPMTAG_VERSION].decode()
 
     @property
     def packager(self):
         """Get the packager of the RPM package"""
-        return self.rpmhdr[librpm.RPMTAG_PACKAGER]
+        return _decode(self.rpmhdr[librpm.RPMTAG_PACKAGER])
 
     def unpack(self, dest_dir):
         """
@@ -159,13 +164,13 @@ class SpecFile(object):
 
         # Other initializations
         source_header = self._specinfo.packages[0].header
-        self.name = source_header[librpm.RPMTAG_NAME]
-        self.upstreamversion = source_header[librpm.RPMTAG_VERSION]
-        self.release = source_header[librpm.RPMTAG_RELEASE]
+        self.name = source_header[librpm.RPMTAG_NAME].decode()
+        self.upstreamversion = source_header[librpm.RPMTAG_VERSION].decode()
+        self.release = source_header[librpm.RPMTAG_RELEASE].decode()
         # rpm-python returns epoch as 'long', convert that to string
         self.epoch = str(source_header[librpm.RPMTAG_EPOCH]) \
-            if source_header[librpm.RPMTAG_EPOCH] != None else None
-        self.packager = source_header[librpm.RPMTAG_PACKAGER]
+            if source_header[librpm.RPMTAG_EPOCH] is not None else None
+        self.packager = _decode(source_header[librpm.RPMTAG_PACKAGER])
         self._tags = {}
         self._special_directives = defaultdict(list)
         self._gbp_tags = defaultdict(list)
@@ -184,7 +189,7 @@ class SpecFile(object):
     def _parse_filtered_spec(self, skip_tags):
         """Parse a filtered spec file in rpm-python"""
         skip_tags = [tag.lower() for tag in skip_tags]
-        with tempfile.NamedTemporaryFile(prefix='gbp') as filtered:
+        with tempfile.NamedTemporaryFile(prefix='gbp', mode='w+') as filtered:
             filtered.writelines(str(line) for line in self._content
                     if str(line).split(":")[0].strip().lower() not in skip_tags)
             filtered.flush()
@@ -303,7 +308,7 @@ class SpecFile(object):
             tagvalue = None
         # We don't support "multivalue" tags like "Provides:" or "SourceX:"
         # Rpm python doesn't support many of these, thus the explicit list
-        if type(tagvalue) is int or type(tagvalue) is int:
+        if isinstance(tagvalue, int):
             tagvalue = str(tagvalue)
         elif type(tagvalue) is list or tagname in self._listtags:
             tagvalue = None
@@ -853,11 +858,11 @@ def guess_spec_repo(repo, treeish, topdir='', recursive=True, preferred_name=Non
     """
     topdir = topdir.rstrip('/') + ('/') if topdir else ''
     try:
-        file_list = [nam for (mod, typ, sha, nam) in
-                    repo.list_tree(treeish, recursive, topdir) if typ == 'blob']
+        file_list = [nam.decode() for (mod, typ, sha, nam) in
+                     repo.list_tree(treeish, recursive, topdir) if typ == 'blob']
     except GitRepositoryError as err:
         raise NoSpecError("Cannot find spec file from treeish %s, Git error: %s"
-                            % (treeish, err))
+                          % (treeish, err))
     spec_path = guess_spec_fn(file_list, preferred_name)
     return spec_from_repo(repo, treeish, spec_path)
 
@@ -865,7 +870,7 @@ def guess_spec_repo(repo, treeish, topdir='', recursive=True, preferred_name=Non
 def spec_from_repo(repo, treeish, spec_path):
     """Get and parse a spec file from a give Git treeish"""
     try:
-        spec = SpecFile(filedata=repo.show('%s:%s' % (treeish, spec_path)))
+        spec = SpecFile(filedata=repo.show('%s:%s' % (treeish, spec_path)).decode())
         spec.specdir = os.path.dirname(spec_path)
         spec.specfile = os.path.basename(spec_path)
         return spec
index 4d22342..c622f60 100644 (file)
@@ -193,7 +193,7 @@ class LinkedList(collections.abc.Iterable):
         'foo'
         >>> [str(data) for data in list]
         ['foo', 'bar']
-        >>> print "%s" % node3
+        >>> print("%s" % node3)
         <BLANKLINE>
         >>> str(list.delete(node1))
         'bar'
index a212286..12eede9 100644 (file)
@@ -150,8 +150,9 @@ def dump_tree(repo, export_dir, treeish, with_submodules, recursive=True):
     if recursive:
         paths = ''
     else:
-        paths = [nam for _mod, typ, _sha, nam in repo.list_tree(treeish) if
-                    typ == 'blob']
+        paths = ["'%s'" % nam.decode() for _mod, typ, _sha, nam in
+                 repo.list_tree(treeish) if typ == 'blob']
+
     try:
         data = repo.archive('tar', '', None, treeish, paths)
         untar_data(export_dir, data)
index 30df610..5551668 100644 (file)
@@ -26,7 +26,7 @@ import gbp.log
 
 # Try to import readline, since that will cause raw_input to get fancy
 # line editing and history capabilities. However, if readline is not
-# available, raw_input will still work.
+# available, input() will still work.
 try:
     import readline
 except ImportError:
@@ -48,7 +48,7 @@ def ask_package_name(default, name_validator_func, err_msg):
     """
     while True:
         sourcepackage = input("What will be the source package name? [%s] " % default)
-        if not sourcepackage: # No input, use the default.
+        if not sourcepackage:  # No input, use the default.
             sourcepackage = default
         # Valid package name, return it.
         if name_validator_func(sourcepackage):
@@ -67,7 +67,7 @@ def ask_package_version(default, ver_validator_func, err_msg):
     """
     while True:
         version = input("What is the upstream version? [%s] " % default)
-        if not version: # No input, use the default.
+        if not version:  # No input, use the default.
             version = default
         # Valid version, return it.
         if ver_validator_func(version):
index 60157b2..0ea9f9e 100644 (file)
@@ -151,7 +151,7 @@ def pq_branch_base(pq_branch, options):
 def parse_gbp_commands(info, cmd_tag, noarg_cmds, arg_cmds):
     """Parse gbp commands from commit message"""
     cmd_re = re.compile(r'^%s:\s*(?P<cmd>[a-z-]+)(\s+(?P<args>\S.*))?' %
-                            cmd_tag, flags=re.I)
+                        cmd_tag, flags=re.I)
     commands = {}
     other_lines = []
     for line in info['body'].splitlines():
@@ -168,7 +168,7 @@ def parse_gbp_commands(info, cmd_tag, noarg_cmds, arg_cmds):
                 commands[cmd] = match.group('args')
             else:
                 gbp.log.warn("Ignoring unknown gbp-command '%s' in commit %s"
-                                % (line, info['id']))
+                             % (line, info['id']))
         else:
             other_lines.append(line)
     return commands, other_lines
@@ -196,7 +196,7 @@ def write_patch_file(filename, commit_info, diff):
         gbp.log.debug("I won't generate empty diff %s" % filename)
         return None
     try:
-        with open(filename, 'w') as patch:
+        with open(filename, 'wb') as patch:
             msg = Message()
             charset = Charset('utf-8')
             charset.body_encoding = None
@@ -208,13 +208,13 @@ def write_patch_file(filename, commit_info, diff):
             # Git compat: put name in quotes if special characters found
             if re.search("[,.@()\[\]\\\:;]", name):
                 name = '"%s"' % name
-            from_header = Header(str(name, 'utf-8'), charset, 77, 'from')
-            from_header.append(str('<%s>' % email))
+            from_header = Header(name.encode('utf-8'), charset, 77, 'from')
+            from_header.append(email.encode('utf-8'))
             msg['From'] = from_header
             date = commit_info['author'].datetime
             datestr = date.strftime('%a, %-d %b %Y %H:%M:%S %z')
-            msg['Date'] = Header(str(datestr, 'utf-8'), charset, 77, 'date')
-            msg['Subject'] = Header(str(commit_info['subject'], 'utf-8'),
+            msg['Date'] = Header(datestr.encode('utf-8'), charset, 77, 'date')
+            msg['Subject'] = Header(commit_info['subject'].encode('utf-8'),
                                     charset, 77, 'subject')
             # Write message body
             if commit_info['body']:
@@ -224,11 +224,11 @@ def write_patch_file(filename, commit_info, diff):
                     msg.set_payload(body.encode('ascii'))
                 except UnicodeDecodeError:
                     msg.set_payload(body, charset)
-            patch.write(msg.as_string(unixfrom=False))
+            patch.write(msg.as_string(unixfrom=False).encode('utf-8'))
 
             # Write diff
-            patch.write('---\n')
-            patch.write(diff)
+            patch.write(b'---\n')
+            patch.write(diff.encode())
     except IOError as err:
         raise GbpError('Unable to create patch file: %s' % err)
     return filename
@@ -278,7 +278,7 @@ def format_diff(outdir, filename, repo, start, end, path_exclude_regex=None):
     info['subject'] = "Raw diff %s..%s" % (start, end)
     info['body'] = ("Raw diff between %s '%s' and\n%s '%s'\n" %
                     (repo.get_obj_type(start), start,
-                    repo.get_obj_type(end), end))
+                     repo.get_obj_type(end), end))
     if not filename:
         filename = '%s-to-%s.diff' % (start, end)
     filename = os.path.join(outdir, filename)
@@ -323,7 +323,7 @@ def get_maintainer_from_control(repo):
                               stdout=subprocess.PIPE).stdout.readlines()
 
     if len(cmdout) > 0:
-        maintainer = cmdout[0].strip()
+        maintainer = cmdout[0].decode().strip()
         m = re.match('(?P<name>.*[^ ]) *<(?P<email>.*)>', maintainer)
         if m:
             return GitModifier(m.group('name'), m.group('email'))
@@ -361,7 +361,7 @@ def apply_and_commit_patch(repo, patch, fallback_author, topic=None):
     """apply a single patch 'patch', add topic 'topic' and commit it"""
     author = {'name': patch.author,
               'email': patch.email,
-              'date': patch.date }
+              'date': patch.date}
 
     patch_fn = os.path.basename(patch.path)
     if not (author['name'] and author['email']):
index c37cea8..7ed5e70 100644 (file)
@@ -1,6 +1,6 @@
 # vim: set fileencoding=utf-8 :
 #
-# (C) 2008, 2009, 2010 Guido Guenther <agx@sigxcpu.org>
+# (C) 2008, 2009, 2010, 2017 Guido Günther <agx@sigxcpu.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; either version 2 of the License, or
@@ -12,9 +12,9 @@
 #    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-"""Import multiple dsc files into GIT in one go"""
+#    along with this program; if not, please see
+#    <http://www.gnu.org/licenses/>
+"""Import multiple dsc files into Git in one go"""
 
 import glob
 import os
@@ -37,6 +37,32 @@ class DscCompareVersions(DpkgCompareVersions):
         return DpkgCompareVersions.__call__(self, dsc1.version, dsc2.version)
 
 
+def cmp_to_key(mycmp):
+    'Convert a cmp= function into a key= function'
+    class K(object):
+        def __init__(self, obj, *args):
+            self.obj = obj
+
+        def __lt__(self, other):
+            return mycmp(self.obj, other.obj) < 0
+
+        def __gt__(self, other):
+            return mycmp(self.obj, other.obj) > 0
+
+        def __eq__(self, other):
+            return mycmp(self.obj, other.obj) == 0
+
+        def __le__(self, other):
+            return mycmp(self.obj, other.obj) <= 0
+
+        def __ge__(self, other):
+            return mycmp(self.obj, other.obj) >= 0
+
+        def __ne__(self, other):
+            return mycmp(self.obj, other.obj) != 0
+    return K
+
+
 class GitImportDsc(object):
     def __init__(self, args):
         self.args = args
@@ -46,7 +72,7 @@ class GitImportDsc(object):
 
 
 def fetch_snapshots(pkg, downloaddir):
-    "Fetch snapshots using debsnap von snapshots.debian.org"
+    "Fetch snapshots using debsnap from snapshots.debian.org"
     dscs = None
 
     gbp.log.info("Downloading snapshots of '%s' to '%s'..." %
@@ -93,7 +119,7 @@ def main(argv):
     dscs = []
     ret = 0
     verbose = False
-    dsc_cmp = DscCompareVersions()
+    dsc_key = cmp_to_key(DscCompareVersions())
     use_debsnap = False
 
     try:
@@ -128,9 +154,9 @@ def main(argv):
 
         if use_debsnap:
             dirs['tmp'] = os.path.abspath(tempfile.mkdtemp())
-            dscs = [ DscFile.parse(f) for f in fetch_snapshots(pkg, dirs['tmp']) ]
+            dscs = [DscFile.parse(f) for f in fetch_snapshots(pkg, dirs['tmp'])]
 
-        dscs.sort(cmp=dsc_cmp)
+        dscs.sort(key=dsc_key)
         importer = GitImportDsc(import_args)
 
         try:
index 0712288..88ca481 100755 (executable)
@@ -81,7 +81,7 @@ def download_file(target_dir, url):
 def download_source(pkg, dirs):
     """Download package from a remote location"""
     if re.match(r'[a-z]{1,5}://', pkg):
-        mode = 'python urllib2'
+        mode = 'python urllib'
     else:
         mode = 'yumdownloader'
 
index 029e718..4430d5f 100755 (executable)
@@ -167,9 +167,9 @@ def export_patches(repo, branch, options):
     pq_branch = pq_branch_name(branch, options)
     try:
         shutil.rmtree(PATCH_DIR)
-    except OSError as msg:
-        if msg.errno != errno.ENOENT:
-            raise GbpError("Failed to remove patch dir: %s" % msg)
+    except OSError as e:
+        if e.errno != errno.ENOENT:
+            raise GbpError("Failed to remove patch dir: %s" % e.strerror)
         else:
             gbp.log.debug("%s does not exist." % PATCH_DIR)
 
index 011c416..92954ee 100755 (executable)
@@ -292,8 +292,8 @@ def safe_patches(queue, tmpdir_base):
     safequeue = PatchSeries()
 
     if len(queue) > 0:
-        gbp.log.debug("Safeing patches '%s' in '%s'" %
-                        (os.path.dirname(queue[0].path), tmpdir))
+        gbp.log.debug("Saving patches '%s' in '%s'" %
+                      (os.path.dirname(queue[0].path), tmpdir))
     for patch in queue:
         base, _archive_fmt, comp = parse_archive_filename(patch.path)
         uncompressors = {'gzip': gzip.open, 'bzip2': bz2.BZ2File}
@@ -308,11 +308,11 @@ def safe_patches(queue, tmpdir_base):
             raise GbpError("Unsupported patch compression '%s', giving up"
                            % comp)
         else:
-            src = open(patch.path, 'r')
+            src = open(patch.path, 'rb')
             dst_name = os.path.join(tmpdir, os.path.basename(patch.path))
 
-        dst = open(dst_name, 'w')
-        dst.writelines(src)
+        dst = open(dst_name, 'wb')
+        dst.write(src.read())
         src.close()
         dst.close()
         if _archive_fmt:
index e5fddb4..b2a836a 100755 (executable)
@@ -92,7 +92,8 @@ def load_customizations(customization_file):
         return
     customizations = {}
     try:
-        exec(compile(open(customization_file, "rb").read(), customization_file, 'exec'), customizations, customizations)
+        with open(customization_file) as f:
+            exec(f.read(), customizations, customizations)
     except Exception as err:
         raise GbpError("Failed to load customization file: %s" % err)
 
index 7a91625..7e35877 100644 (file)
@@ -53,7 +53,7 @@ def version(prog):
         from gbp.version import gbp_version
     except ImportError:
         gbp_version = '[Unknown version]'
-    print(("%s %s" % (os.path.basename(prog), gbp_version)))
+    print("%s %s" % (os.path.basename(prog), gbp_version))
 
 
 def import_command(cmd):
@@ -62,7 +62,7 @@ def import_command(cmd):
     """
     modulename = sanitize(cmd)
     if (not re.match(r'[a-z][a-z0-9_]', modulename) or
-        modulename in invalid_modules):
+            modulename in invalid_modules):
         raise ImportError('Illegal module name %s' % modulename)
 
     return __import__('gbp.scripts.%s' % modulename, fromlist='main', level=0)
@@ -90,7 +90,7 @@ def list_available_commands():
     path = os.path.dirname(mod.__file__)
     maxlen = 0
 
-    print(("Available commands in %s\n" % path))
+    print("Available commands in %s\n" % path)
     cmds = sorted(get_available_commands(path))
     for cmd in cmds:
         if len(cmd[0]) > maxlen:
@@ -98,7 +98,7 @@ def list_available_commands():
     for cmd in cmds:
         mod = import_command(cmd[0])
         doc = mod.__doc__
-        print(("    %s - %s" % (cmd[0].rjust(maxlen), doc)))
+        print("    %s - %s" % (cmd[0].rjust(maxlen), doc))
     print('')
 
 
index 3132dc0..f680d4e 100755 (executable)
@@ -151,7 +151,7 @@ Debian and the RPM tool set.
 
 
 %build
-WITHOUT_NOSETESTS=1 python3 ./setup.py build
+WITHOUT_NOSETESTS=1 %{__python3} ./setup.py build
 
 %if %{with docs}
 # Prepare apidocs
@@ -168,13 +168,13 @@ HAVE_SGML2X=0 make -C docs/
 GIT_CEILING_DIRECTORIES=%{_builddir} \
     GIT_AUTHOR_EMAIL=rpmbuild@example.com GIT_AUTHOR_NAME=rpmbuild \
     GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL \
-    python3 setup.py nosetests
+    %{__python3} setup.py nosetests
 %endif
 
 
 %install
 rm -rf %{buildroot}
-WITHOUT_NOSETESTS=1 python3 ./setup.py install --root=%{buildroot} --prefix=/usr
+WITHOUT_NOSETESTS=1  %{__python3} ./setup.py install --root=%{buildroot} --prefix=/usr
 rm -rf %{buildroot}%{python3_sitelib}/*info
 
 #remove __pycache directory
index 4bf75f5..a0a94b9 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ def fetch_version():
     try:
         popen = subprocess.Popen('dpkg-parsechangelog', stdout=subprocess.PIPE)
         out, ret = popen.communicate()
-        for line in out.decode().split('\n'):
+        for line in out.decode('utf-8').split('\n'):
             if line.startswith('Version:'):
                 version = line.split(' ')[1].strip()
                 break
@@ -38,7 +38,7 @@ def fetch_version():
 
     with open('gbp/version.py', 'w') as f:
         f.write('"The current gbp version number"\n')
-        f.write('gbp_version="%s"\n' % version)
+        f.write('gbp_version = "%s"\n' % version)
 
     return version
 
@@ -49,7 +49,7 @@ def readme():
 
 setup(name = "gbp",
       version = fetch_version(),
-      author = 'Guido Günther',
+      author=u'Guido Günther',
       author_email = 'agx@sigxcpu.org',
       url = 'https://honk.sigxcpu.org/piki/projects/git-buildpackage/',
       description = 'Suite to help with Debian packages in Git repositories',
@@ -57,8 +57,8 @@ setup(name = "gbp",
       long_description = readme(),
       classifiers = [
           'Environment :: Console',
-          'Programming Language :: Python :: 2',
-          'Topic :: Software Development :: Version Control :: Git',
+          'Programming Language :: Python :: 3',
+          'Topic :: Software Development :: Version Control',
           'Operating System :: POSIX :: Linux',
       ],
       scripts = [ 'bin/git-buildpackage',
@@ -78,6 +78,11 @@ setup(name = "gbp",
                   'bin/git-rpm-ch'],
       packages = find_packages(exclude=['tests', 'tests.*']),
       data_files = [("/etc/git-buildpackage/", ["gbp.conf"]),],
+      requires=['dateutil'],
+      install_requires=[
+          'python-dateutil',
+      ],
+      python_requires='>=3.5',
       setup_requires=['nose>=0.11.1', 'coverage>=2.85'] if \
                         os.getenv('WITHOUT_NOSETESTS') is None else [],
       entry_points = {
index a421eb8..4fe279e 100644 (file)
@@ -24,7 +24,7 @@ class TestHelp(unittest.TestCase):
                       'pull',
                       'pq']:
             module = 'gbp.scripts.%s' % script
-            m = __import__(module, globals(), locals(), ['main'], -1)
+            m = __import__(module, globals(), locals(), ['main'], 0)
             self.assertRaises(SystemExit,
                               m.main,
                               ['doesnotmatter', '--help'])
@@ -36,7 +36,7 @@ class TestHelp(unittest.TestCase):
                        'buildpackage_rpm',
                        'import_orig_rpm']:
             module = 'gbp.scripts.%s' % script
-            m = __import__(module, globals(), locals(), ['main'], -1)
+            m = __import__(module, globals(), locals(), ['main'], 0)
             self.assertRaises(SystemExit,
                               m.main,
                               ['doesnotmatter', '--help'])
index 0a14d38..c3ca818 100644 (file)
@@ -5,8 +5,6 @@
 from . import context
 
 import os
-import shutil
-import tempfile
 
 import gbp.log
 import gbp.git
@@ -16,40 +14,47 @@ fastimport = None
 tf_name = 'testfile'
 tl_name = 'a_testlink'
 
+
 def setup():
     global repo
 
     tmpdir = context.new_tmpdir(__name__)
     repo = gbp.git.GitRepository.create(tmpdir.join('test_repo'))
 
+
 def teardown():
     context.teardown()
 
+
 def test_init_fastimport():
     """Create a fastimport object"""
     global fastimport
     fastimport = gbp.git.FastImport(repo)
     assert fastimport, "Failed to init FastImport"
 
+
 def test_add_file():
     """Add a file via fastimport"""
     author = repo.get_author_info()
     fastimport.start_commit('master', author, "a commit")
     fastimport.deleteall()
     testfile = os.path.join(repo.path, '.git', 'description')
-    fastimport.add_file('./testfile',
-                        open(testfile),
+    fastimport.add_file(b'./testfile',
+                        open(testfile, 'rb'),
                         os.path.getsize(testfile))
 
+
 def test_add_symlink():
     """Add a symbolic link via fastimport"""
     author = repo.get_author_info()
     fastimport.start_commit('master', author, "a 2nd commit")
     fastimport.add_symlink(tl_name, tf_name)
 
+
 def test_close():
     fastimport.close()
 
+
 def test_result():
     repo.force_head('master', hard=True)
 
@@ -59,4 +64,3 @@ def test_result():
     assert os.path.exists(testfile), "%s doesn't exist" % testfile
     assert os.path.lexists(testlink), "%s doesn't exist" % testlink
     assert os.readlink(testlink) == tf_name
-
index 9ae636e..f051181 100644 (file)
@@ -3,11 +3,9 @@
 """Test  L{GitRepository}'s write_tree method"""
 
 from . import context
-
+from . import testutils
 import os
 
-import tests.testutils as testutils
-
 import gbp.log
 import gbp.git
 import gbp.errors
index f489f2b..df87756 100644 (file)
@@ -4,7 +4,7 @@
 
 from . import context
 
-import tests.testutils as testutils
+from . import testutils
 
 import gbp.errors
 import gbp.scripts.buildpackage as buildpackage
index 9bdc5c5..9394b8f 100644 (file)
@@ -4,15 +4,9 @@
 
 from . import context
 
-# Try unittest2 for CentOS
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
-
-from tests.testutils import (DebianGitTestRepo, OsReleaseFile,
-                             get_dch_default_urgency)
-
+from .testutils import (DebianGitTestRepo, OsReleaseFile,
+                        get_dch_default_urgency)
+import unittest
 from gbp.scripts import dch
 
 import os
index 2be3097..2c937df 100644 (file)
@@ -4,12 +4,10 @@
 
 from . import context
 
-import os, tempfile
-# Try unittest2 for CentOS
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
+import os
+import tempfile
+import platform
+import unittest
 
 import gbp.deb
 
@@ -53,7 +51,7 @@ Files:
 
     def setUp(self):
         with tempfile.NamedTemporaryFile(delete=False) as self.dscfile:
-            self.dscfile.write(self.content)
+            self.dscfile.write(self.content.encode())
 
     def tearDown(self):
         os.unlink(self.dscfile.name)
index f71c542..6af51ee 100644 (file)
 """Test L{gbp.pq}"""
 
 from . import context
+from . import testutils
 
 import os
 import logging
-# Try unittest2 for CentOS
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
+import unittest
 
 from gbp.scripts.pq import generate_patches, switch_pq, export_patches
 import gbp.scripts.common.pq as pq
-import gbp.patch_series
-import tests.testutils as testutils
 
 class TestApplyAndCommit(testutils.DebianGitTestRepo):
     """Test L{gbp.pq}'s apply_and_commit"""
@@ -42,7 +37,7 @@ class TestApplyAndCommit(testutils.DebianGitTestRepo):
         patch = gbp.patch_series.Patch(_patch_path('foo.patch'))
 
         pq.apply_and_commit_patch(self.repo, patch, None)
-        self.assertIn('foo', self.repo.list_files())
+        self.assertIn(b'foo', self.repo.list_files())
 
 
     def test_topic(self):
@@ -61,7 +56,7 @@ class TestApplyAndCommit(testutils.DebianGitTestRepo):
         """
         def _check_log(msg):
             self.assertEqual(msg, "Patch 'foo.patch' has no authorship "
-                         "information, using 'Guido Günther <gg@godiug.net>'")
+                             "information, using 'Guido Günther <gg@godiug.net>'")
 
         patch = gbp.patch_series.Patch(_patch_path('foo.patch'))
 
@@ -72,7 +67,8 @@ class TestApplyAndCommit(testutils.DebianGitTestRepo):
 
         # Fake a control file
         self.add_file("debian/control",
-                      "Maintainer: Guido Günther <gg@godiug.net>")
+                      "Maintainer: Guido Günther <gg@godiug.net>".encode('utf-8'),
+                      mode='wb+')
 
         maintainer = pq.get_maintainer_from_control(self.repo)
         orig_warn = gbp.log.warn
@@ -81,7 +77,8 @@ class TestApplyAndCommit(testutils.DebianGitTestRepo):
         gbp.log.warn = orig_warn
         info = self.repo.get_commit_info('HEAD')
         self.assertEqual(info['author'].email, 'gg@godiug.net')
-        self.assertIn('foo', self.repo.list_files())
+        self.assertIn(b'foo', self.repo.list_files())
+
 
 class TestApplySinglePatch(testutils.DebianGitTestRepo):
     """Test L{gbp.pq}'s apply_single_patch"""
@@ -97,7 +94,7 @@ class TestApplySinglePatch(testutils.DebianGitTestRepo):
 
         dummy_opts = object()
         pq.apply_single_patch(self.repo, 'master', patch, None, dummy_opts)
-        self.assertIn('foo', self.repo.list_files())
+        self.assertIn(b'foo', self.repo.list_files())
 
 class TestWritePatch(testutils.DebianGitTestRepo):
     """Test L{gbp.pq}'s write_patch """
@@ -126,7 +123,7 @@ class TestWritePatch(testutils.DebianGitTestRepo):
         expected = os.path.join(str(d), 'gbptest', 'added-foo.patch')
 
         self.assertTrue(os.path.exists(expected))
-        logging.debug(file(expected).read())
+        logging.debug(open(expected).read())
 
         # Reapply the patch to a new branch
         self.repo.create_branch('testapply', 'HEAD^')
index 2fdc2e6..eab896b 100644 (file)
@@ -68,7 +68,7 @@ class TestImportDscs(testutils.DebianGitTestRepo):
 
     def _check_err_msg(self, err):
         self.assertIsInstance(err, GbpError)
-        self.assertIn("Failed to import", err.message)
+        self.assertIn("Failed to import", str(err))
 
     def test_import_success(self):
         """Test importing success with stub"""
index a4ea556..aa2776a 100644 (file)
@@ -16,9 +16,9 @@
 """Test L{gbp.pq}"""
 
 from . import context
+from . import testutils
 
 import os
-from . import testutils
 from gbp.deb.source import DebianSource, DebianSourceError
 from gbp.deb.format import DebianSourceFormat
 from gbp.git.vfs import GitVfs
index 0bb7354..ba1a42d 100644 (file)
@@ -1,11 +1,8 @@
 # vim: set fileencoding=utf-8 :
 
 import os
-# Try unittest2 for CentOS
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
+
+import unittest
 from gbp.config import GbpOptionParser, GbpOptionGroup
 from . import context
 
index 66a5e2f..f1522f9 100644 (file)
@@ -43,7 +43,7 @@ class ComponentTestGitRepository(GitRepository):
             raise GitRepositoryError("Cannot get submodule status: %s" %
                                      err.strip())
         submodules = {}
-        for line in out.splitlines():
+        for line in out.decode().splitlines():
             module = line.strip()
             # Uninitialized
             status = module[0]
index 49773e8..6589132 100644 (file)
@@ -269,7 +269,7 @@ class TestGbpRpm(RpmRepoTestBase):
         # Dummy update to upstream branch
         pkg_branch = repo.get_branch()
         upstr_branch = 'srcdata/gbp-test/upstream'
-        orig_files = ['gbp-test/' + path for \
+        orig_files = ['gbp-test/' +  path.decode() for \
                 path in self.ls_tree(repo, upstr_branch)] + ['gbp-test']
         repo.set_branch(upstr_branch)
         with open('new-file', 'w') as fobj:
@@ -409,7 +409,7 @@ class TestGbpRpm(RpmRepoTestBase):
         repo.set_branch(pkg_branch)
 
         sub_files = self.ls_tree(sub_repo, 'HEAD')
-        upstr_files = ['gbp-test/' + path for
+        upstr_files = ['gbp-test/' + path.decode() for
                             path in self.ls_tree(repo, upstr_branch)]
 
         # Test the "no" option
@@ -423,7 +423,7 @@ class TestGbpRpm(RpmRepoTestBase):
         eq_(mock_gbp(['--git-submodules', '--git-upstream-tree=%s' %
                       upstr_branch, '--git-ignore-untracked']), 0)
         tar_files = ls_tar('../rpmbuild/SOURCES/gbp-test-1.1.tar.bz2', False)
-        ref_files = upstr_files + ['gbp-test/gbp-test-native.repo/' + path for
+        ref_files = upstr_files + ['gbp-test/gbp-test-native.repo/' + path.decode() for
                                         path in sub_files]
         self.check_files(ref_files, tar_files)
         shutil.rmtree('../rpmbuild')
@@ -444,7 +444,7 @@ class TestGbpRpm(RpmRepoTestBase):
         repo.commit_all('Add submodule')
 
         sub_files = self.ls_tree(sub_repo, 'HEAD')
-        master_files = ['gbp-test-native-1.0/' + path for
+        master_files = ['gbp-test-native-1.0/' + path.decode() for
                             path in self.ls_tree(repo, 'HEAD')]
 
         # Test
index 7dcfe22..48cbfb3 100644 (file)
@@ -288,7 +288,7 @@ class TestDownloadImport(ComponentTestBase):
         # Mock to use local files instead of really downloading
         local_fn = os.path.join(DATA_DIR, os.path.basename(srpm))
         urllib.request.urlopen = Mock()
-        urllib.request.urlopen.return_value = open(local_fn, 'r')
+        urllib.request.urlopen.return_value = open(local_fn, 'rb')
 
         eq_(mock_import(['--no-pristine-tar', '--download', srpm]), 0)
         # Check repository state
index c14ea68..83230cf 100644 (file)
@@ -96,7 +96,7 @@ class TestPqRpm(RpmRepoTestBase):
                  'gbp-test.spec', '0001-my-gz.patch', '0002-my-bzip2.patch',
                  '0003-my2.patch', 'my.patch']
         self._check_repo_state(repo, 'master', branches, files)
-        eq_(repo.status()[' M'], ['gbp-test.spec'])
+        eq_(repo.status()[' M'], [b'gbp-test.spec'])
 
         # Another export after removing some patches
         os.unlink('0001-my-gz.patch')
@@ -118,7 +118,7 @@ class TestPqRpm(RpmRepoTestBase):
         # Test export
         eq_(mock_pq(['export']), 0)
         self._check_repo_state(repo, 'master-orphan', branches)
-        eq_(repo.status()[' M'], ['packaging/gbp-test2.spec'])
+        eq_(repo.status()[' M'], [b'packaging/gbp-test2.spec'])
 
     def test_import_in_subdir(self):
         """Test running gbp-rpm-pq from a subdir in the git tree"""
@@ -202,11 +202,11 @@ class TestPqRpm(RpmRepoTestBase):
     def test_force_import(self):
         """Test force import"""
         repo = self.init_test_repo('gbp-test')
-        pkg_files = repo.list_files()
+        pkg_files = [f.decode() for f in repo.list_files()]
         repo.rename_branch('pq/master', 'development/master')
         repo.set_branch('development/master')
         branches = repo.get_local_branches()
-        pq_files = repo.list_files()
+        pq_files = [f.decode() for f in repo.list_files()]
 
         # Re-import should fail
         eq_(mock_pq(['import']), 1)
@@ -486,7 +486,7 @@ class TestPqRpm(RpmRepoTestBase):
         repo.commit_dir('.', 'Merge with master', 'development/master',
                         ['master'])
         merge_rev = repo.rev_parse('HEAD', short=7)
-        eq_(mock_pq(['apply', patches[0]]), 0)
+        eq_(mock_pq(['apply', patches[0].decode()]), 0)
         upstr_rev = repo.rev_parse('upstream', short=7)
         os.unlink(patches[0])
 
index 30f25e5..b717761 100644 (file)
@@ -229,7 +229,7 @@ def test_add_section():
     >>> import tempfile
     >>> import shutil
     >>> import gbp.deb.changelog
-    >>> from tests.testutils import OsReleaseFile
+    >>> from .testutils import OsReleaseFile
     >>> os_release = OsReleaseFile('/etc/lsb-release')
     >>> olddir = os.path.abspath(os.path.curdir)
     >>> testdir = tempfile.mkdtemp(prefix='gbp-test-changelog-')
@@ -275,7 +275,7 @@ def test_add_entry():
     >>> import tempfile
     >>> import shutil
     >>> import gbp.deb.changelog
-    >>> from tests.testutils import OsReleaseFile
+    >>> from .testutils import OsReleaseFile
     >>> os_release = OsReleaseFile('/etc/lsb-release')
     >>> olddir = os.path.abspath(os.path.curdir)
     >>> testdir = tempfile.mkdtemp(prefix='gbp-test-changelog-')
index 0a05d4b..8e7930e 100644 (file)
@@ -19,10 +19,14 @@ def test_author():
     'foo'
     >>> modifier.email
     'bar'
-    >>> modifier.get_author_env()
-    {'GIT_AUTHOR_EMAIL': 'bar', 'GIT_AUTHOR_NAME': 'foo'}
-    >>> modifier.get_committer_env()
-    {'GIT_COMMITTER_NAME': 'foo', 'GIT_COMMITTER_EMAIL': 'bar'}
+    >>> modifier.get_author_env()['GIT_AUTHOR_EMAIL']
+    'bar'
+    >>> modifier.get_author_env()['GIT_AUTHOR_NAME']
+    'foo'
+    >>> modifier.get_committer_env()['GIT_COMMITTER_NAME']
+    'foo'
+    >>> modifier.get_committer_env()['GIT_COMMITTER_EMAIL']
+    'bar'
     >>> modifier._get_env('foo')
     Traceback (most recent call last):
     ...
index f8cfafe..15651e2 100644 (file)
@@ -82,10 +82,10 @@ def test_add_files():
     >>> repo.is_clean(ignore_untracked=True)[0]
     True
     >>> repo.add_files('testfile', force=True, untracked=False)
-    >>> repo.status().items()
+    >>> list(repo.status().items())
     [('??', ['testfile'])]
     >>> repo.add_files(repo.path, force=True)
-    >>> repo.status().items()
+    >>> list(repo.status().items())
     [('A ', ['testfile'])]
     >>> repo.commit_all(msg="foo")
     >>> repo.is_clean()[0]
@@ -897,16 +897,16 @@ def test_status():
     >>> repo = gbp.git.GitRepository(repo_dir)
     >>> fname = os.path.join(repo.path, "test_status")
     >>> shutil.copy(os.path.join(repo.path, ".git/HEAD"), fname)
-    >>> repo.status().items()
+    >>> list(repo.status().items())
     [('??', ['test_status'])]
-    >>> repo.status(['bla*']).items()
+    >>> list(repo.status().items())
     []
-    >>> repo.status(['te*']).items()
+    >>> list(repo.status().items())
     [('??', ['test_status'])]
     >>> repo.add_files(repo.path, force=True)
     >>> repo.commit_all(msg='added %s' % fname)
     >>> _ = repo._git_inout('mv', [fname, fname + 'new'])
-    >>> repo.status().items()
+    >>> list(repo.status().items())
     [('R ', ['test_status\x00test_statusnew'])]
     """