Răsfoiți Sursa

Port pack to Python3

Gary van der Merwe 10 ani în urmă
părinte
comite
2b5d2db596
3 a modificat fișierele cu 268 adăugiri și 258 ștergeri
  1. 87 64
      dulwich/pack.py
  2. 5 5
      dulwich/tests/compat/test_pack.py
  3. 176 189
      dulwich/tests/test_pack.py

+ 87 - 64
dulwich/pack.py

@@ -38,6 +38,8 @@ from collections import (
     deque,
     )
 import difflib
+import struct
+import operator
 
 from itertools import chain
 try:
@@ -85,6 +87,19 @@ from dulwich.objects import (
     object_header,
     )
 
+if sys.version_info[0] == 2:
+    iteritems = lambda d: d.iteritems()
+    int_types = (int, long)
+    int2byte = chr
+
+    def indexbytes(buf, i):
+        return ord(buf[i])
+else:
+    iteritems = lambda d: d.items()
+    int_types = int
+    int2byte = struct.Struct(">B").pack
+    indexbytes = operator.getitem
+    xrange = range
 
 OFS_DELTA = 6
 REF_DELTA = 7
@@ -105,7 +120,7 @@ def take_msb_bytes(read, crc32=None):
         b = read(1)
         if crc32 is not None:
             crc32 = binascii.crc32(b, crc32)
-        ret.append(ord(b))
+        ret.append(indexbytes(b, 0))
     return ret, crc32
 
 
@@ -261,7 +276,7 @@ def iter_sha1(iter):
     sha = sha1()
     for name in iter:
         sha.update(name)
-    return sha.hexdigest()
+    return sha.hexdigest().encode('ascii')
 
 
 def load_pack_index(path):
@@ -304,8 +319,8 @@ def load_pack_index_file(path, f):
     :return: A PackIndex loaded from the given file
     """
     contents, size = _load_file_contents(f)
-    if contents[:4] == '\377tOc':
-        version = struct.unpack('>L', contents[4:8])[0]
+    if contents[:4] == b'\377tOc':
+        version = struct.unpack(b'>L', contents[4:8])[0]
         if version == 2:
             return PackIndex2(path, file=f, contents=contents,
                 size=size)
@@ -328,10 +343,9 @@ def bisect_find_sha(start, end, sha, unpack_name):
     while start <= end:
         i = (start + end) // 2
         file_sha = unpack_name(i)
-        x = cmp(file_sha, sha)
-        if x < 0:
+        if file_sha < sha:
             start = i + 1
-        elif x > 0:
+        elif file_sha > sha:
             end = i - 1
         else:
             return i
@@ -547,14 +561,14 @@ class FilePackIndex(PackIndex):
 
         :return: 20-byte binary digest
         """
-        return str(self._contents[-40:-20])
+        return bytes(self._contents[-40:-20])
 
     def get_stored_checksum(self):
         """Return the SHA1 checksum stored for this index.
 
         :return: 20-byte binary digest
         """
-        return str(self._contents[-20:])
+        return bytes(self._contents[-20:])
 
     def _object_index(self, sha):
         """See object_index.
@@ -562,7 +576,7 @@ class FilePackIndex(PackIndex):
         :param sha: A *binary* SHA string. (20 characters long)_
         """
         assert len(sha) == 20
-        idx = ord(sha[0])
+        idx = indexbytes(sha, 0)
         if idx == 0:
             start = 0
         else:
@@ -605,9 +619,9 @@ class PackIndex2(FilePackIndex):
 
     def __init__(self, filename, file=None, contents=None, size=None):
         super(PackIndex2, self).__init__(filename, file, contents, size)
-        if self._contents[:4] != '\377tOc':
+        if self._contents[:4] != b'\377tOc':
             raise AssertionError('Not a v2 pack index file')
-        (self.version, ) = unpack_from('>L', self._contents, 4)
+        (self.version, ) = unpack_from(b'>L', self._contents, 4)
         if self.version != 2:
             raise AssertionError('Version was %d' % self.version)
         self._fan_out_table = self._read_fan_out_table(8)
@@ -649,17 +663,20 @@ def read_pack_header(read):
     header = read(12)
     if not header:
         return None, None
-    if header[:4] != 'PACK':
+    if header[:4] != b'PACK':
         raise AssertionError('Invalid pack header %r' % header)
-    (version,) = unpack_from('>L', header, 4)
+    (version,) = unpack_from(b'>L', header, 4)
     if version not in (2, 3):
         raise AssertionError('Version was %d' % version)
-    (num_objects,) = unpack_from('>L', header, 8)
+    (num_objects,) = unpack_from(b'>L', header, 8)
     return (version, num_objects)
 
 
 def chunks_length(chunks):
-    return sum(imap(len, chunks))
+    if isinstance(chunks, bytes):
+        return len(chunks)
+    else:
+        return sum(imap(len, chunks))
 
 
 def unpack_object(read_all, read_some=None, compute_crc32=False,
@@ -777,7 +794,11 @@ class PackStreamReader(object):
             to_add = n
         for _ in xrange(to_pop):
             self.sha.update(self._trailer.popleft())
-        self._trailer.extend(data[-to_add:])
+
+        if sys.version_info[0] == 2:
+            self._trailer.extend(data[-to_add:])
+        else:
+            self._trailer.extend(int2byte(b) for b in data[-to_add:])
 
         # hash everything but the trailer
         self.sha.update(data[:-to_add])
@@ -862,7 +883,7 @@ class PackStreamReader(object):
             # read buffer and (20 - N) come from the wire.
             self.read(20)
 
-        pack_sha = ''.join(self._trailer)
+        pack_sha = b''.join(self._trailer)
         if pack_sha != self.sha.digest():
             raise ChecksumMismatch(sha_to_hex(pack_sha), self.sha.hexdigest())
 
@@ -913,8 +934,11 @@ def obj_sha(type, chunks):
     """Compute the SHA for a numeric type and object chunks."""
     sha = sha1()
     sha.update(object_header(type, chunks_length(chunks)))
-    for chunk in chunks:
-        sha.update(chunk)
+    if isinstance(chunks, bytes):
+        sha.update(chunks)
+    else:
+        for chunk in chunks:
+            sha.update(chunk)
     return sha.digest()
 
 
@@ -1072,7 +1096,7 @@ class PackData(object):
             assert isinstance(type, int)
         elif type == REF_DELTA:
             (basename, delta) = obj
-            assert isinstance(basename, str) and len(basename) == 20
+            assert isinstance(basename, bytes) and len(basename) == 20
             base_offset, type, base_obj = get_ref(basename)
             assert isinstance(type, int)
         type, base_chunks = self.resolve_object(base_offset, type, base_obj)
@@ -1201,8 +1225,7 @@ class PackData(object):
             return self._offset_cache[offset]
         except KeyError:
             pass
-        assert isinstance(offset, long) or isinstance(offset, int),\
-                'offset was %r' % offset
+        assert isinstance(offset, int_types), 'offset was %r' % offset
         assert offset >= self._header_size
         self._file.seek(offset)
         unpacked, _ = unpack_object(self._file.read)
@@ -1281,7 +1304,7 @@ class DeltaChainIterator(object):
             self._ensure_no_pending()
             return
 
-        for base_sha, pending in sorted(self._pending_ref.iteritems()):
+        for base_sha, pending in sorted(iteritems(self._pending_ref)):
             if base_sha not in self._pending_ref:
                 continue
             try:
@@ -1383,7 +1406,7 @@ class SHA1Writer(object):
     def __init__(self, f):
         self.f = f
         self.length = 0
-        self.sha1 = sha1('')
+        self.sha1 = sha1(b'')
 
     def write(self, data):
         self.sha1.update(data)
@@ -1417,14 +1440,14 @@ def pack_object_header(type_num, delta_base, size):
     :param size: Uncompressed object size.
     :return: A header for a packed object.
     """
-    header = ''
+    header = b''
     c = (type_num << 4) | (size & 15)
     size >>= 4
     while size:
-        header += (chr(c | 0x80))
+        header += (int2byte(c | 0x80))
         c = size & 0x7f
         size >>= 7
-    header += chr(c)
+    header += int2byte(c)
     if type_num == OFS_DELTA:
         ret = [delta_base & 0x7f]
         delta_base >>= 7
@@ -1432,7 +1455,7 @@ def pack_object_header(type_num, delta_base, size):
             delta_base -= 1
             ret.insert(0, 0x80 | (delta_base & 0x7f))
             delta_base >>= 7
-        header += ''.join([chr(x) for x in ret])
+        header += b''.join([int2byte(x) for x in ret])
     elif type_num == REF_DELTA:
         assert len(delta_base) == 20
         header += delta_base
@@ -1475,7 +1498,7 @@ def write_pack(filename, objects, deltify=None, delta_window_size=None):
     with GitFile(filename + '.pack', 'wb') as f:
         entries, data_sum = write_pack_objects(f, objects,
             delta_window_size=delta_window_size, deltify=deltify)
-    entries = [(k, v[0], v[1]) for (k, v) in entries.iteritems()]
+    entries = [(k, v[0], v[1]) for (k, v) in iteritems(entries)]
     entries.sort()
     with GitFile(filename + '.idx', 'wb') as f:
         return data_sum, write_pack_index_v2(f, entries, data_sum)
@@ -1483,9 +1506,9 @@ def write_pack(filename, objects, deltify=None, delta_window_size=None):
 
 def write_pack_header(f, num_objects):
     """Write a pack header for the given number of objects."""
-    f.write('PACK')                          # Pack header
-    f.write(struct.pack('>L', 2))            # Pack version
-    f.write(struct.pack('>L', num_objects))  # Number of objects in pack
+    f.write(b'PACK')                          # Pack header
+    f.write(struct.pack(b'>L', 2))            # Pack version
+    f.write(struct.pack(b'>L', num_objects))  # Number of objects in pack
 
 
 def deltify_pack_objects(objects, window_size=None):
@@ -1585,7 +1608,7 @@ def write_pack_index_v1(f, entries, pack_checksum):
     f = SHA1Writer(f)
     fan_out_table = defaultdict(lambda: 0)
     for (name, offset, entry_checksum) in entries:
-        fan_out_table[ord(name[0])] += 1
+        fan_out_table[indexbytes(name, 0)] += 1
     # Fan-out table
     for i in range(0x100):
         f.write(struct.pack('>L', fan_out_table[i]))
@@ -1600,14 +1623,14 @@ def write_pack_index_v1(f, entries, pack_checksum):
 
 
 def _delta_encode_size(size):
-    ret = ''
+    ret = b''
     c = size & 0x7f
     size >>= 7
     while size:
-        ret += chr(c | 0x80)
+        ret += int2byte(c | 0x80)
         c = size & 0x7f
         size >>= 7
-    ret += chr(c)
+    ret += int2byte(c)
     return ret
 
 
@@ -1617,17 +1640,17 @@ def _delta_encode_size(size):
 _MAX_COPY_LEN = 0xffff
 
 def _encode_copy_operation(start, length):
-    scratch = ''
+    scratch = b''
     op = 0x80
     for i in range(4):
         if start & 0xff << i*8:
-            scratch += chr((start >> i*8) & 0xff)
+            scratch += int2byte((start >> i*8) & 0xff)
             op |= 1 << i
     for i in range(2):
         if length & 0xff << i*8:
-            scratch += chr((length >> i*8) & 0xff)
+            scratch += int2byte((length >> i*8) & 0xff)
             op |= 1 << (4+i)
-    return chr(op) + scratch
+    return int2byte(op) + scratch
 
 
 def create_delta(base_buf, target_buf):
@@ -1636,10 +1659,10 @@ def create_delta(base_buf, target_buf):
     :param base_buf: Base buffer
     :param target_buf: Target buffer
     """
-    assert isinstance(base_buf, str)
-    assert isinstance(target_buf, str)
-    out_buf = ''
-     # write delta header
+    assert isinstance(base_buf, bytes)
+    assert isinstance(target_buf, bytes)
+    out_buf = b''
+    # write delta header
     out_buf += _delta_encode_size(len(base_buf))
     out_buf += _delta_encode_size(len(target_buf))
     # write out delta opcodes
@@ -1664,11 +1687,11 @@ def create_delta(base_buf, target_buf):
             s = j2 - j1
             o = j1
             while s > 127:
-                out_buf += chr(127)
+                out_buf += int2byte(127)
                 out_buf += target_buf[o:o+127]
                 s -= 127
                 o += 127
-            out_buf += chr(s)
+            out_buf += int2byte(s)
             out_buf += target_buf[o:o+s]
     return out_buf
 
@@ -1679,10 +1702,10 @@ def apply_delta(src_buf, delta):
     :param src_buf: Source buffer
     :param delta: Delta instructions
     """
-    if not isinstance(src_buf, str):
-        src_buf = ''.join(src_buf)
-    if not isinstance(delta, str):
-        delta = ''.join(delta)
+    if not isinstance(src_buf, bytes):
+        src_buf = b''.join(src_buf)
+    if not isinstance(delta, bytes):
+        delta = b''.join(delta)
     out = []
     index = 0
     delta_length = len(delta)
@@ -1690,7 +1713,7 @@ def apply_delta(src_buf, delta):
         size = 0
         i = 0
         while delta:
-            cmd = ord(delta[index])
+            cmd = indexbytes(delta, index)
             index += 1
             size |= (cmd & ~0x80) << i
             i += 7
@@ -1701,20 +1724,20 @@ def apply_delta(src_buf, delta):
     dest_size, index = get_delta_header_size(delta, index)
     assert src_size == len(src_buf), '%d vs %d' % (src_size, len(src_buf))
     while index < delta_length:
-        cmd = ord(delta[index])
+        cmd = indexbytes(delta, index)
         index += 1
         if cmd & 0x80:
             cp_off = 0
             for i in range(4):
                 if cmd & (1 << i):
-                    x = ord(delta[index])
+                    x = indexbytes(delta, index)
                     index += 1
                     cp_off |= x << (i * 8)
             cp_size = 0
             # Version 3 packs can contain copy sizes larger than 64K.
             for i in range(3):
                 if cmd & (1 << (4+i)):
-                    x = ord(delta[index])
+                    x = indexbytes(delta, index)
                     index += 1
                     cp_size |= x << (i * 8)
             if cp_size == 0:
@@ -1749,28 +1772,28 @@ def write_pack_index_v2(f, entries, pack_checksum):
     :return: The SHA of the index file written
     """
     f = SHA1Writer(f)
-    f.write('\377tOc') # Magic!
+    f.write(b'\377tOc')  # Magic!
     f.write(struct.pack('>L', 2))
     fan_out_table = defaultdict(lambda: 0)
     for (name, offset, entry_checksum) in entries:
-        fan_out_table[ord(name[0])] += 1
+        fan_out_table[indexbytes(name, 0)] += 1
     # Fan-out table
     largetable = []
     for i in range(0x100):
-        f.write(struct.pack('>L', fan_out_table[i]))
+        f.write(struct.pack(b'>L', fan_out_table[i]))
         fan_out_table[i+1] += fan_out_table[i]
     for (name, offset, entry_checksum) in entries:
         f.write(name)
     for (name, offset, entry_checksum) in entries:
-        f.write(struct.pack('>L', entry_checksum))
+        f.write(struct.pack(b'>L', entry_checksum))
     for (name, offset, entry_checksum) in entries:
         if offset < 2**31:
-            f.write(struct.pack('>L', offset))
+            f.write(struct.pack(b'>L', offset))
         else:
-            f.write(struct.pack('>L', 2**31 + len(largetable)))
+            f.write(struct.pack(b'>L', 2**31 + len(largetable)))
             largetable.append(offset)
     for offset in largetable:
-        f.write(struct.pack('>Q', offset))
+        f.write(struct.pack(b'>Q', offset))
     assert len(pack_checksum) == 20
     f.write(pack_checksum)
     return f.write_sha()
@@ -1890,7 +1913,7 @@ class Pack(object):
         offset = self.index.object_index(sha1)
         obj_type, obj = self.data.get_object_at(offset)
         type_num, chunks = self.data.resolve_object(offset, obj_type, obj)
-        return type_num, ''.join(chunks)
+        return type_num, b''.join(chunks)
 
     def __getitem__(self, sha1):
         """Retrieve the specified SHA1."""
@@ -1932,7 +1955,7 @@ class Pack(object):
         with GitFile(keepfile_name, 'wb') as keepfile:
             if msg:
                 keepfile.write(msg)
-                keepfile.write('\n')
+                keepfile.write(b'\n')
         return keepfile_name
 
 

+ 5 - 5
dulwich/tests/compat/test_pack.py

@@ -45,7 +45,7 @@ from dulwich.tests.compat.utils import (
     run_git_or_fail,
     )
 
-_NON_DELTA_RE = re.compile('non delta: (?P<non_delta>\d+) objects')
+_NON_DELTA_RE = re.compile(b'non delta: (?P<non_delta>\d+) objects')
 
 def _git_verify_pack_object_list(output):
     pack_shas = set()
@@ -81,7 +81,7 @@ class TestPack(PackTests):
         orig_pack = self.get_pack(pack1_sha)
         orig_blob = orig_pack[a_sha]
         new_blob = Blob()
-        new_blob.data = orig_blob.data + 'x'
+        new_blob.data = orig_blob.data + b'x'
         all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None)]
         pack_path = os.path.join(self._tempdir, "pack_with_deltas")
         write_pack(pack_path, all_to_pack, deltify=True)
@@ -102,9 +102,9 @@ class TestPack(PackTests):
         orig_pack = self.get_pack(pack1_sha)
         orig_blob = orig_pack[a_sha]
         new_blob = Blob()
-        new_blob.data = orig_blob.data + ('x' * 2 ** 20)
+        new_blob.data = orig_blob.data + (b'x' * 2 ** 20)
         new_blob_2 = Blob()
-        new_blob_2.data = new_blob.data + 'y'
+        new_blob_2.data = new_blob.data + b'y'
         all_to_pack = list(orig_pack.pack_tuples()) + [(new_blob, None),
                                                        (new_blob_2, None)]
         pack_path = os.path.join(self._tempdir, "pack_with_deltas")
@@ -121,7 +121,7 @@ class TestPack(PackTests):
             'Expected 3 non-delta objects, got %d' % got_non_delta)
         # We expect one object to have a delta chain length of two
         # (new_blob_2), so let's verify that actually happens:
-        self.assertIn('chain length = 2', output)
+        self.assertIn(b'chain length = 2', output)
 
     # This test is SUPER slow: over 80 seconds on a 2012-era
     # laptop. This is because SequenceMatcher is worst-case quadratic

+ 176 - 189
dulwich/tests/test_pack.py

@@ -77,14 +77,13 @@ from dulwich.tests.utils import (
     skipIfPY3,
     )
 
-pack1_sha = 'bc63ddad95e7321ee734ea11a7a62d314e0d7481'
+pack1_sha = b'bc63ddad95e7321ee734ea11a7a62d314e0d7481'
 
-a_sha = '6f670c0fb53f9463760b7295fbb814e965fb20c8'
-tree_sha = 'b2a2766a2879c209ab1176e7e778b81ae422eeaa'
-commit_sha = 'f18faa16531ac570a3fdc8c7ca16682548dafd12'
+a_sha = b'6f670c0fb53f9463760b7295fbb814e965fb20c8'
+tree_sha = b'b2a2766a2879c209ab1176e7e778b81ae422eeaa'
+commit_sha = b'f18faa16531ac570a3fdc8c7ca16682548dafd12'
 
 
-@skipIfPY3
 class PackTests(TestCase):
     """Base class for testing packs"""
 
@@ -98,14 +97,14 @@ class PackTests(TestCase):
 
     def get_pack_index(self, sha):
         """Returns a PackIndex from the datadir with the given sha"""
-        return load_pack_index(os.path.join(self.datadir, 'pack-%s.idx' % sha))
+        return load_pack_index(os.path.join(self.datadir, 'pack-%s.idx' % sha.decode('ascii')))
 
     def get_pack_data(self, sha):
         """Returns a PackData object from the datadir with the given sha"""
-        return PackData(os.path.join(self.datadir, 'pack-%s.pack' % sha))
+        return PackData(os.path.join(self.datadir, 'pack-%s.pack' % sha.decode('ascii')))
 
     def get_pack(self, sha):
-        return Pack(os.path.join(self.datadir, 'pack-%s' % sha))
+        return Pack(os.path.join(self.datadir, 'pack-%s' % sha.decode('ascii')))
 
     def assertSucceeds(self, func, *args, **kwargs):
         try:
@@ -114,7 +113,6 @@ class PackTests(TestCase):
             self.fail(e)
 
 
-@skipIfPY3
 class PackIndexTests(PackTests):
     """Class that tests the index of packfiles"""
 
@@ -132,10 +130,10 @@ class PackIndexTests(PackTests):
 
     def test_get_stored_checksum(self):
         p = self.get_pack_index(pack1_sha)
-        self.assertEqual('f2848e2ad16f329ae1c92e3b95e91888daa5bd01',
-                          sha_to_hex(p.get_stored_checksum()))
-        self.assertEqual('721980e866af9a5f93ad674144e1459b8ba3e7b7',
-                          sha_to_hex(p.get_pack_checksum()))
+        self.assertEqual(b'f2848e2ad16f329ae1c92e3b95e91888daa5bd01',
+                         sha_to_hex(p.get_stored_checksum()))
+        self.assertEqual(b'721980e866af9a5f93ad674144e1459b8ba3e7b7',
+                         sha_to_hex(p.get_pack_checksum()))
 
     def test_index_check(self):
         p = self.get_pack_index(pack1_sha)
@@ -145,30 +143,29 @@ class PackIndexTests(PackTests):
         p = self.get_pack_index(pack1_sha)
         entries = [(sha_to_hex(s), o, c) for s, o, c in p.iterentries()]
         self.assertEqual([
-          ('6f670c0fb53f9463760b7295fbb814e965fb20c8', 178, None),
-          ('b2a2766a2879c209ab1176e7e778b81ae422eeaa', 138, None),
-          ('f18faa16531ac570a3fdc8c7ca16682548dafd12', 12, None)
-          ], entries)
+            (b'6f670c0fb53f9463760b7295fbb814e965fb20c8', 178, None),
+            (b'b2a2766a2879c209ab1176e7e778b81ae422eeaa', 138, None),
+            (b'f18faa16531ac570a3fdc8c7ca16682548dafd12', 12, None)
+        ], entries)
 
     def test_iter(self):
         p = self.get_pack_index(pack1_sha)
         self.assertEqual(set([tree_sha, commit_sha, a_sha]), set(p))
 
 
-@skipIfPY3
 class TestPackDeltas(TestCase):
 
-    test_string1 = 'The answer was flailing in the wind'
-    test_string2 = 'The answer was falling down the pipe'
-    test_string3 = 'zzzzz'
+    test_string1 = b'The answer was flailing in the wind'
+    test_string2 = b'The answer was falling down the pipe'
+    test_string3 = b'zzzzz'
 
-    test_string_empty = ''
-    test_string_big = 'Z' * 8192
-    test_string_huge = 'Z' * 100000
+    test_string_empty = b''
+    test_string_big = b'Z' * 8192
+    test_string_huge = b'Z' * 100000
 
     def _test_roundtrip(self, base, target):
         self.assertEqual(target,
-                          ''.join(apply_delta(base, create_delta(base, target))))
+                          b''.join(apply_delta(base, create_delta(base, target))))
 
     def test_nochange(self):
         self._test_roundtrip(self.test_string1, self.test_string1)
@@ -195,13 +192,12 @@ class TestPackDeltas(TestCase):
     def test_dest_overflow(self):
         self.assertRaises(
             ApplyDeltaError,
-            apply_delta, 'a'*0x10000, '\x80\x80\x04\x80\x80\x04\x80' + 'a'*0x10000)
+            apply_delta, b'a'*0x10000, b'\x80\x80\x04\x80\x80\x04\x80' + b'a'*0x10000)
         self.assertRaises(
             ApplyDeltaError,
-            apply_delta, '', '\x00\x80\x02\xb0\x11\x11')
+            apply_delta, b'', b'\x00\x80\x02\xb0\x11\x11')
 
 
-@skipIfPY3
 class TestPackData(PackTests):
     """Tests getting the data from the packfile."""
 
@@ -209,8 +205,9 @@ class TestPackData(PackTests):
         self.get_pack_data(pack1_sha).close()
 
     def test_from_file(self):
-        path = os.path.join(self.datadir, 'pack-%s.pack' % pack1_sha)
-        PackData.from_file(open(path), os.path.getsize(path))
+        path = os.path.join(self.datadir, 'pack-%s.pack' % pack1_sha.decode('ascii'))
+        with open(path, 'rb') as f:
+            PackData.from_file(f, os.path.getsize(path))
 
     def test_pack_len(self):
         with self.get_pack_data(pack1_sha) as p:
@@ -222,31 +219,31 @@ class TestPackData(PackTests):
 
     def test_iterobjects(self):
         with self.get_pack_data(pack1_sha) as p:
-            commit_data = ('tree b2a2766a2879c209ab1176e7e778b81ae422eeaa\n'
-                           'author James Westby <jw+debian@jameswestby.net> '
-                           '1174945067 +0100\n'
-                           'committer James Westby <jw+debian@jameswestby.net> '
-                           '1174945067 +0100\n'
-                           '\n'
-                           'Test commit\n')
-            blob_sha = '6f670c0fb53f9463760b7295fbb814e965fb20c8'
-            tree_data = '100644 a\0%s' % hex_to_sha(blob_sha)
+            commit_data = (b'tree b2a2766a2879c209ab1176e7e778b81ae422eeaa\n'
+                           b'author James Westby <jw+debian@jameswestby.net> '
+                           b'1174945067 +0100\n'
+                           b'committer James Westby <jw+debian@jameswestby.net> '
+                           b'1174945067 +0100\n'
+                           b'\n'
+                           b'Test commit\n')
+            blob_sha = b'6f670c0fb53f9463760b7295fbb814e965fb20c8'
+            tree_data = b'100644 a\0' + hex_to_sha(blob_sha)
             actual = []
             for offset, type_num, chunks, crc32 in p.iterobjects():
-                actual.append((offset, type_num, ''.join(chunks), crc32))
+                actual.append((offset, type_num, b''.join(chunks), crc32))
             self.assertEqual([
-              (12, 1, commit_data, 3775879613),
-              (138, 2, tree_data, 912998690),
-              (178, 3, 'test 1\n', 1373561701)
-              ], actual)
+                (12, 1, commit_data, 3775879613),
+                (138, 2, tree_data, 912998690),
+                (178, 3, b'test 1\n', 1373561701)
+                ], actual)
 
     def test_iterentries(self):
         with self.get_pack_data(pack1_sha) as p:
             entries = set((sha_to_hex(s), o, c) for s, o, c in p.iterentries())
             self.assertEqual(set([
-              ('6f670c0fb53f9463760b7295fbb814e965fb20c8', 178, 1373561701),
-              ('b2a2766a2879c209ab1176e7e778b81ae422eeaa', 138, 912998690),
-              ('f18faa16531ac570a3fdc8c7ca16682548dafd12', 12, 3775879613),
+              (b'6f670c0fb53f9463760b7295fbb814e965fb20c8', 178, 1373561701),
+              (b'b2a2766a2879c209ab1176e7e778b81ae422eeaa', 138, 912998690),
+              (b'f18faa16531ac570a3fdc8c7ca16682548dafd12', 12, 3775879613),
               ]), entries)
 
     def test_create_index_v1(self):
@@ -266,28 +263,27 @@ class TestPackData(PackTests):
             self.assertEqual(idx1, idx2)
 
     def test_compute_file_sha(self):
-        f = BytesIO('abcd1234wxyz')
-        self.assertEqual(sha1('abcd1234wxyz').hexdigest(),
+        f = BytesIO(b'abcd1234wxyz')
+        self.assertEqual(sha1(b'abcd1234wxyz').hexdigest(),
                          compute_file_sha(f).hexdigest())
-        self.assertEqual(sha1('abcd1234wxyz').hexdigest(),
+        self.assertEqual(sha1(b'abcd1234wxyz').hexdigest(),
                          compute_file_sha(f, buffer_size=5).hexdigest())
-        self.assertEqual(sha1('abcd1234').hexdigest(),
+        self.assertEqual(sha1(b'abcd1234').hexdigest(),
                          compute_file_sha(f, end_ofs=-4).hexdigest())
-        self.assertEqual(sha1('1234wxyz').hexdigest(),
+        self.assertEqual(sha1(b'1234wxyz').hexdigest(),
                          compute_file_sha(f, start_ofs=4).hexdigest())
         self.assertEqual(
-          sha1('1234').hexdigest(),
-          compute_file_sha(f, start_ofs=4, end_ofs=-4).hexdigest())
+            sha1(b'1234').hexdigest(),
+            compute_file_sha(f, start_ofs=4, end_ofs=-4).hexdigest())
 
     def test_compute_file_sha_short_file(self):
-        f = BytesIO('abcd1234wxyz')
+        f = BytesIO(b'abcd1234wxyz')
         self.assertRaises(AssertionError, compute_file_sha, f, end_ofs=-20)
         self.assertRaises(AssertionError, compute_file_sha, f, end_ofs=20)
         self.assertRaises(AssertionError, compute_file_sha, f, start_ofs=10,
             end_ofs=-12)
 
 
-@skipIfPY3
 class TestPack(PackTests):
 
     def test_len(self):
@@ -323,14 +319,14 @@ class TestPack(PackTests):
         """Tests random access for non-delta objects"""
         with self.get_pack(pack1_sha) as p:
             obj = p[a_sha]
-            self.assertEqual(obj.type_name, 'blob')
-            self.assertEqual(obj.sha().hexdigest(), a_sha)
+            self.assertEqual(obj.type_name, b'blob')
+            self.assertEqual(obj.sha().hexdigest().encode('ascii'), a_sha)
             obj = p[tree_sha]
-            self.assertEqual(obj.type_name, 'tree')
-            self.assertEqual(obj.sha().hexdigest(), tree_sha)
+            self.assertEqual(obj.type_name, b'tree')
+            self.assertEqual(obj.sha().hexdigest().encode('ascii'), tree_sha)
             obj = p[commit_sha]
-            self.assertEqual(obj.type_name, 'commit')
-            self.assertEqual(obj.sha().hexdigest(), commit_sha)
+            self.assertEqual(obj.type_name, b'commit')
+            self.assertEqual(obj.sha().hexdigest().encode('ascii'), commit_sha)
 
     def test_copy(self):
         with self.get_pack(pack1_sha) as origpack:
@@ -353,7 +349,7 @@ class TestPack(PackTests):
     def test_commit_obj(self):
         with self.get_pack(pack1_sha) as p:
             commit = p[commit_sha]
-            self.assertEqual('James Westby <jw+debian@jameswestby.net>',
+            self.assertEqual(b'James Westby <jw+debian@jameswestby.net>',
                              commit.author)
             self.assertEqual([], commit.parents)
 
@@ -380,7 +376,7 @@ class TestPack(PackTests):
         with self.get_pack(pack1_sha) as p:
             p = self._copy_pack(p)
 
-        msg = 'some message'
+        msg = b'some message'
         with p:
             keepfile_name = p.keep(msg)
 
@@ -388,9 +384,9 @@ class TestPack(PackTests):
         self.assertTrue(os.path.exists(keepfile_name))
 
         # and contain the right message, with a linefeed
-        with open(keepfile_name, 'r') as f:
+        with open(keepfile_name, 'rb') as f:
             buf = f.read()
-            self.assertEqual(msg + '\n', buf)
+            self.assertEqual(msg + b'\n', buf)
 
     def test_name(self):
         with self.get_pack(pack1_sha) as p:
@@ -418,7 +414,7 @@ class TestPack(PackTests):
             Pack.from_objects(data, index).check_length_and_checksum()
 
             data._file.seek(0)
-            bad_file = BytesIO(data._file.read()[:-20] + ('\xff' * 20))
+            bad_file = BytesIO(data._file.read()[:-20] + (b'\xff' * 20))
             bad_data = PackData('', file=bad_file)
             bad_pack = Pack.from_lazy_objects(lambda: bad_data, lambda: index)
             self.assertRaises(ChecksumMismatch, lambda: bad_pack.data)
@@ -435,17 +431,16 @@ class TestPack(PackTests):
             self.assertTrue(isinstance(objs[commit_sha], Commit))
 
 
-@skipIfPY3
 class TestThinPack(PackTests):
 
     def setUp(self):
         super(TestThinPack, self).setUp()
         self.store = MemoryObjectStore()
         self.blobs = {}
-        for blob in ('foo', 'bar', 'foo1234', 'bar2468'):
+        for blob in (b'foo', b'bar', b'foo1234', b'bar2468'):
             self.blobs[blob] = make_object(Blob, data=blob)
-        self.store.add_object(self.blobs['foo'])
-        self.store.add_object(self.blobs['bar'])
+        self.store.add_object(self.blobs[b'foo'])
+        self.store.add_object(self.blobs[b'bar'])
 
         # Build a thin pack. 'foo' is as an external reference, 'bar' an
         # internal reference.
@@ -455,9 +450,9 @@ class TestThinPack(PackTests):
 
         with open(self.pack_prefix + '.pack', 'wb') as f:
             build_pack(f, [
-                (REF_DELTA, (self.blobs['foo'].id, 'foo1234')),
-                (Blob.type_num, 'bar'),
-                (REF_DELTA, (self.blobs['bar'].id, 'bar2468'))],
+                (REF_DELTA, (self.blobs[b'foo'].id, b'foo1234')),
+                (Blob.type_num, b'bar'),
+                (REF_DELTA, (self.blobs[b'bar'].id, b'bar2468'))],
                 store=self.store)
 
         # Index the new pack.
@@ -466,7 +461,7 @@ class TestThinPack(PackTests):
                 data.pack = pack
                 data.create_index(self.pack_prefix + '.idx')
 
-        del self.store[self.blobs['bar'].id]
+        del self.store[self.blobs[b'bar'].id]
 
     def make_pack(self, resolve_ext_ref):
         return Pack(
@@ -476,54 +471,53 @@ class TestThinPack(PackTests):
     def test_get_raw(self):
         with self.make_pack(False) as p:
             self.assertRaises(
-                KeyError, p.get_raw, self.blobs['foo1234'].id)
+                KeyError, p.get_raw, self.blobs[b'foo1234'].id)
         with self.make_pack(True) as p:
             self.assertEqual(
-                (3, 'foo1234'),
-                p.get_raw(self.blobs['foo1234'].id))
+                (3, b'foo1234'),
+                p.get_raw(self.blobs[b'foo1234'].id))
 
     def test_iterobjects(self):
         with self.make_pack(False) as p:
             self.assertRaises(KeyError, list, p.iterobjects())
         with self.make_pack(True) as p:
             self.assertEqual(
-                sorted([self.blobs['foo1234'].id, self.blobs[b'bar'].id,
-                        self.blobs['bar2468'].id]),
+                sorted([self.blobs[b'foo1234'].id, self.blobs[b'bar'].id,
+                        self.blobs[b'bar2468'].id]),
                 sorted(o.id for o in p.iterobjects()))
 
 
-@skipIfPY3
 class WritePackTests(TestCase):
 
     def test_write_pack_header(self):
         f = BytesIO()
         write_pack_header(f, 42)
-        self.assertEqual('PACK\x00\x00\x00\x02\x00\x00\x00*',
-                f.getvalue())
+        self.assertEqual(b'PACK\x00\x00\x00\x02\x00\x00\x00*',
+                         f.getvalue())
 
     def test_write_pack_object(self):
         f = BytesIO()
-        f.write('header')
+        f.write(b'header')
         offset = f.tell()
-        crc32 = write_pack_object(f, Blob.type_num, 'blob')
+        crc32 = write_pack_object(f, Blob.type_num, b'blob')
         self.assertEqual(crc32, zlib.crc32(f.getvalue()[6:]) & 0xffffffff)
 
-        f.write('x')  # unpack_object needs extra trailing data.
+        f.write(b'x')  # unpack_object needs extra trailing data.
         f.seek(offset)
         unpacked, unused = unpack_object(f.read, compute_crc32=True)
         self.assertEqual(Blob.type_num, unpacked.pack_type_num)
         self.assertEqual(Blob.type_num, unpacked.obj_type_num)
-        self.assertEqual(['blob'], unpacked.decomp_chunks)
+        self.assertEqual([b'blob'], unpacked.decomp_chunks)
         self.assertEqual(crc32, unpacked.crc32)
-        self.assertEqual('x', unused)
+        self.assertEqual(b'x', unused)
 
     def test_write_pack_object_sha(self):
         f = BytesIO()
-        f.write('header')
+        f.write(b'header')
         offset = f.tell()
-        sha_a = sha1('foo')
+        sha_a = sha1(b'foo')
         sha_b = sha_a.copy()
-        write_pack_object(f, Blob.type_num, 'blob', sha=sha_a)
+        write_pack_object(f, Blob.type_num, b'blob', sha=sha_a)
         self.assertNotEqual(sha_a.digest(), sha_b.digest())
         sha_b.update(f.getvalue()[offset:])
         self.assertEqual(sha_a.digest(), sha_b.digest())
@@ -613,7 +607,6 @@ class BaseTestFilePackIndexWriting(BaseTestPackIndexWriting):
             self._write_fn(f, entries, pack_checksum)
 
 
-@skipIfPY3
 class TestMemoryIndexWriting(TestCase, BaseTestPackIndexWriting):
 
     def setUp(self):
@@ -628,7 +621,6 @@ class TestMemoryIndexWriting(TestCase, BaseTestPackIndexWriting):
         TestCase.tearDown(self)
 
 
-@skipIfPY3
 class TestPackIndexWritingv1(TestCase, BaseTestFilePackIndexWriting):
 
     def setUp(self):
@@ -644,7 +636,6 @@ class TestPackIndexWritingv1(TestCase, BaseTestFilePackIndexWriting):
         BaseTestFilePackIndexWriting.tearDown(self)
 
 
-@skipIfPY3
 class TestPackIndexWritingv2(TestCase, BaseTestFilePackIndexWriting):
 
     def setUp(self):
@@ -660,7 +651,6 @@ class TestPackIndexWritingv2(TestCase, BaseTestFilePackIndexWriting):
         BaseTestFilePackIndexWriting.tearDown(self)
 
 
-@skipIfPY3
 class ReadZlibTests(TestCase):
 
     decomp = (
@@ -671,7 +661,7 @@ class ReadZlibTests(TestCase):
       b'\n'
       b"Provide replacement for mmap()'s offset argument.")
     comp = zlib.compress(decomp)
-    extra = 'nextobject'
+    extra = b'nextobject'
 
     def setUp(self):
         super(ReadZlibTests, self).setUp()
@@ -699,11 +689,11 @@ class ReadZlibTests(TestCase):
 
     def test_decompress_empty(self):
         unpacked = UnpackedObject(Tree.type_num, None, 0, None)
-        comp = zlib.compress('')
+        comp = zlib.compress(b'')
         read = BytesIO(comp + self.extra).read
         unused = read_zlib_chunks(read, unpacked)
-        self.assertEqual('', ''.join(unpacked.decomp_chunks))
-        self.assertNotEqual('', unused)
+        self.assertEqual(b'', b''.join(unpacked.decomp_chunks))
+        self.assertNotEqual(b'', unused)
         self.assertEqual(self.extra, unused + read())
 
     def test_decompress_no_crc32(self):
@@ -714,9 +704,9 @@ class ReadZlibTests(TestCase):
     def _do_decompress_test(self, buffer_size, **kwargs):
         unused = read_zlib_chunks(self.read, self.unpacked,
                                   buffer_size=buffer_size, **kwargs)
-        self.assertEqual(self.decomp, ''.join(self.unpacked.decomp_chunks))
+        self.assertEqual(self.decomp, b''.join(self.unpacked.decomp_chunks))
         self.assertEqual(zlib.crc32(self.comp), self.unpacked.crc32)
-        self.assertNotEqual('', unused)
+        self.assertNotEqual(b'', unused)
         self.assertEqual(self.extra, unused + self.read())
 
     def test_simple_decompress(self):
@@ -739,33 +729,31 @@ class ReadZlibTests(TestCase):
 
     def test_decompress_include_comp(self):
         self._do_decompress_test(4096, include_comp=True)
-        self.assertEqual(self.comp, ''.join(self.unpacked.comp_chunks))
+        self.assertEqual(self.comp, b''.join(self.unpacked.comp_chunks))
 
 
-@skipIfPY3
 class DeltifyTests(TestCase):
 
     def test_empty(self):
         self.assertEqual([], list(deltify_pack_objects([])))
 
     def test_single(self):
-        b = Blob.from_string("foo")
+        b = Blob.from_string(b"foo")
         self.assertEqual(
             [(b.type_num, b.sha().digest(), None, b.as_raw_string())],
-            list(deltify_pack_objects([(b, "")])))
+            list(deltify_pack_objects([(b, b"")])))
 
     def test_simple_delta(self):
-        b1 = Blob.from_string("a" * 101)
-        b2 = Blob.from_string("a" * 100)
+        b1 = Blob.from_string(b"a" * 101)
+        b2 = Blob.from_string(b"a" * 100)
         delta = create_delta(b1.as_raw_string(), b2.as_raw_string())
         self.assertEqual([
             (b1.type_num, b1.sha().digest(), None, b1.as_raw_string()),
             (b2.type_num, b2.sha().digest(), b1.sha().digest(), delta)
             ],
-            list(deltify_pack_objects([(b1, ""), (b2, "")])))
+            list(deltify_pack_objects([(b1, b""), (b2, b"")])))
 
 
-@skipIfPY3
 class TestPackStreamReader(TestCase):
 
     def test_read_objects_emtpy(self):
@@ -777,9 +765,9 @@ class TestPackStreamReader(TestCase):
     def test_read_objects(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (Blob.type_num, 'blob'),
-          (OFS_DELTA, (0, 'blob1')),
-          ])
+            (Blob.type_num, b'blob'),
+            (OFS_DELTA, (0, b'blob1')),
+        ])
         reader = PackStreamReader(f.read)
         objects = list(reader.read_objects(compute_crc32=True))
         self.assertEqual(2, len(objects))
@@ -790,7 +778,7 @@ class TestPackStreamReader(TestCase):
         self.assertEqual(Blob.type_num, unpacked_blob.pack_type_num)
         self.assertEqual(Blob.type_num, unpacked_blob.obj_type_num)
         self.assertEqual(None, unpacked_blob.delta_base)
-        self.assertEqual('blob', ''.join(unpacked_blob.decomp_chunks))
+        self.assertEqual(b'blob', b''.join(unpacked_blob.decomp_chunks))
         self.assertEqual(entries[0][4], unpacked_blob.crc32)
 
         self.assertEqual(entries[1][0], unpacked_delta.offset)
@@ -798,16 +786,16 @@ class TestPackStreamReader(TestCase):
         self.assertEqual(None, unpacked_delta.obj_type_num)
         self.assertEqual(unpacked_delta.offset - unpacked_blob.offset,
                          unpacked_delta.delta_base)
-        delta = create_delta('blob', 'blob1')
-        self.assertEqual(delta, ''.join(unpacked_delta.decomp_chunks))
+        delta = create_delta(b'blob', b'blob1')
+        self.assertEqual(delta, b''.join(unpacked_delta.decomp_chunks))
         self.assertEqual(entries[1][4], unpacked_delta.crc32)
 
     def test_read_objects_buffered(self):
         f = BytesIO()
         build_pack(f, [
-          (Blob.type_num, 'blob'),
-          (OFS_DELTA, (0, 'blob1')),
-          ])
+            (Blob.type_num, b'blob'),
+            (OFS_DELTA, (0, b'blob1')),
+        ])
         reader = PackStreamReader(f.read, zlib_bufsize=4)
         self.assertEqual(2, len(list(reader.read_objects())))
 
@@ -816,7 +804,6 @@ class TestPackStreamReader(TestCase):
         self.assertEqual([], list(reader.read_objects()))
 
 
-@skipIfPY3
 class TestPackIterator(DeltaChainIterator):
 
     _compute_crc32 = True
@@ -828,7 +815,7 @@ class TestPackIterator(DeltaChainIterator):
     def _result(self, unpacked):
         """Return entries in the same format as build_pack."""
         return (unpacked.offset, unpacked.obj_type_num,
-                ''.join(unpacked.obj_chunks), unpacked.sha(), unpacked.crc32)
+                b''.join(unpacked.obj_chunks), unpacked.sha(), unpacked.crc32)
 
     def _resolve_object(self, offset, pack_type_num, base_chunks):
         assert offset not in self._unpacked_offsets, (
@@ -838,7 +825,6 @@ class TestPackIterator(DeltaChainIterator):
           offset, pack_type_num, base_chunks)
 
 
-@skipIfPY3
 class DeltaChainIteratorTests(TestCase):
 
     def setUp(self):
@@ -862,6 +848,7 @@ class DeltaChainIteratorTests(TestCase):
         self.fetched.add(hex_sha)
         return self.store.get_raw(hex_sha)
 
+    @skipIfPY3
     def make_pack_iter(self, f, thin=None):
         if thin is None:
             thin = bool(list(self.store))
@@ -877,46 +864,46 @@ class DeltaChainIteratorTests(TestCase):
     def test_no_deltas(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (Commit.type_num, 'commit'),
-          (Blob.type_num, 'blob'),
-          (Tree.type_num, 'tree'),
-          ])
+            (Commit.type_num, b'commit'),
+            (Blob.type_num, b'blob'),
+            (Tree.type_num, b'tree'),
+        ])
         self.assertEntriesMatch([0, 1, 2], entries, self.make_pack_iter(f))
 
     def test_ofs_deltas(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (Blob.type_num, 'blob'),
-          (OFS_DELTA, (0, 'blob1')),
-          (OFS_DELTA, (0, 'blob2')),
-          ])
+            (Blob.type_num, b'blob'),
+            (OFS_DELTA, (0, b'blob1')),
+            (OFS_DELTA, (0, b'blob2')),
+        ])
         self.assertEntriesMatch([0, 1, 2], entries, self.make_pack_iter(f))
 
     def test_ofs_deltas_chain(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (Blob.type_num, 'blob'),
-          (OFS_DELTA, (0, 'blob1')),
-          (OFS_DELTA, (1, 'blob2')),
-          ])
+            (Blob.type_num, b'blob'),
+            (OFS_DELTA, (0, b'blob1')),
+            (OFS_DELTA, (1, b'blob2')),
+        ])
         self.assertEntriesMatch([0, 1, 2], entries, self.make_pack_iter(f))
 
     def test_ref_deltas(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (1, 'blob1')),
-          (Blob.type_num, ('blob')),
-          (REF_DELTA, (1, 'blob2')),
-          ])
+            (REF_DELTA, (1, b'blob1')),
+            (Blob.type_num, (b'blob')),
+            (REF_DELTA, (1, b'blob2')),
+        ])
         self.assertEntriesMatch([1, 0, 2], entries, self.make_pack_iter(f))
 
     def test_ref_deltas_chain(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (2, 'blob1')),
-          (Blob.type_num, ('blob')),
-          (REF_DELTA, (1, 'blob2')),
-          ])
+            (REF_DELTA, (2, b'blob1')),
+            (Blob.type_num, (b'blob')),
+            (REF_DELTA, (1, b'blob2')),
+        ])
         self.assertEntriesMatch([1, 2, 0], entries, self.make_pack_iter(f))
 
     def test_ofs_and_ref_deltas(self):
@@ -924,58 +911,58 @@ class DeltaChainIteratorTests(TestCase):
         # this ref.
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (1, 'blob1')),
-          (Blob.type_num, ('blob')),
-          (OFS_DELTA, (1, 'blob2')),
-          ])
+            (REF_DELTA, (1, b'blob1')),
+            (Blob.type_num, (b'blob')),
+            (OFS_DELTA, (1, b'blob2')),
+        ])
         self.assertEntriesMatch([1, 2, 0], entries, self.make_pack_iter(f))
 
     def test_mixed_chain(self):
         f = BytesIO()
         entries = build_pack(f, [
-          (Blob.type_num, 'blob'),
-          (REF_DELTA, (2, 'blob2')),
-          (OFS_DELTA, (0, 'blob1')),
-          (OFS_DELTA, (1, 'blob3')),
-          (OFS_DELTA, (0, 'bob')),
-          ])
+            (Blob.type_num, b'blob'),
+            (REF_DELTA, (2, b'blob2')),
+            (OFS_DELTA, (0, b'blob1')),
+            (OFS_DELTA, (1, b'blob3')),
+            (OFS_DELTA, (0, b'bob')),
+        ])
         self.assertEntriesMatch([0, 2, 1, 3, 4], entries,
                                 self.make_pack_iter(f))
 
     def test_long_chain(self):
         n = 100
-        objects_spec = [(Blob.type_num, 'blob')]
+        objects_spec = [(Blob.type_num, b'blob')]
         for i in range(n):
-            objects_spec.append((OFS_DELTA, (i, 'blob%i' % i)))
+            objects_spec.append((OFS_DELTA, (i, b'blob' + str(i).encode('ascii'))))
         f = BytesIO()
         entries = build_pack(f, objects_spec)
         self.assertEntriesMatch(range(n + 1), entries, self.make_pack_iter(f))
 
     def test_branchy_chain(self):
         n = 100
-        objects_spec = [(Blob.type_num, 'blob')]
+        objects_spec = [(Blob.type_num, b'blob')]
         for i in range(n):
-            objects_spec.append((OFS_DELTA, (0, 'blob%i' % i)))
+            objects_spec.append((OFS_DELTA, (0, b'blob' + str(i).encode('ascii'))))
         f = BytesIO()
         entries = build_pack(f, objects_spec)
         self.assertEntriesMatch(range(n + 1), entries, self.make_pack_iter(f))
 
     def test_ext_ref(self):
-        blob, = self.store_blobs(['blob'])
+        blob, = self.store_blobs([b'blob'])
         f = BytesIO()
-        entries = build_pack(f, [(REF_DELTA, (blob.id, 'blob1'))],
+        entries = build_pack(f, [(REF_DELTA, (blob.id, b'blob1'))],
                              store=self.store)
         pack_iter = self.make_pack_iter(f)
         self.assertEntriesMatch([0], entries, pack_iter)
         self.assertEqual([hex_to_sha(blob.id)], pack_iter.ext_refs())
 
     def test_ext_ref_chain(self):
-        blob, = self.store_blobs(['blob'])
+        blob, = self.store_blobs([b'blob'])
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (1, 'blob2')),
-          (REF_DELTA, (blob.id, 'blob1')),
-          ], store=self.store)
+            (REF_DELTA, (1, b'blob2')),
+            (REF_DELTA, (blob.id, b'blob1')),
+        ], store=self.store)
         pack_iter = self.make_pack_iter(f)
         self.assertEntriesMatch([1, 0], entries, pack_iter)
         self.assertEqual([hex_to_sha(blob.id)], pack_iter.ext_refs())
@@ -983,46 +970,46 @@ class DeltaChainIteratorTests(TestCase):
     def test_ext_ref_chain_degenerate(self):
         # Test a degenerate case where the sender is sending a REF_DELTA
         # object that expands to an object already in the repository.
-        blob, = self.store_blobs(['blob'])
-        blob2, = self.store_blobs(['blob2'])
+        blob, = self.store_blobs([b'blob'])
+        blob2, = self.store_blobs([b'blob2'])
         assert blob.id < blob2.id
 
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (blob.id, 'blob2')),
-          (REF_DELTA, (0, 'blob3')),
+          (REF_DELTA, (blob.id, b'blob2')),
+          (REF_DELTA, (0, b'blob3')),
           ], store=self.store)
         pack_iter = self.make_pack_iter(f)
         self.assertEntriesMatch([0, 1], entries, pack_iter)
         self.assertEqual([hex_to_sha(blob.id)], pack_iter.ext_refs())
 
     def test_ext_ref_multiple_times(self):
-        blob, = self.store_blobs(['blob'])
+        blob, = self.store_blobs([b'blob'])
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (blob.id, 'blob1')),
-          (REF_DELTA, (blob.id, 'blob2')),
-          ], store=self.store)
+            (REF_DELTA, (blob.id, b'blob1')),
+            (REF_DELTA, (blob.id, b'blob2')),
+        ], store=self.store)
         pack_iter = self.make_pack_iter(f)
         self.assertEntriesMatch([0, 1], entries, pack_iter)
         self.assertEqual([hex_to_sha(blob.id)], pack_iter.ext_refs())
 
     def test_multiple_ext_refs(self):
-        b1, b2 = self.store_blobs(['foo', 'bar'])
+        b1, b2 = self.store_blobs([b'foo', b'bar'])
         f = BytesIO()
         entries = build_pack(f, [
-          (REF_DELTA, (b1.id, 'foo1')),
-          (REF_DELTA, (b2.id, 'bar2')),
-          ], store=self.store)
+            (REF_DELTA, (b1.id, b'foo1')),
+            (REF_DELTA, (b2.id, b'bar2')),
+        ], store=self.store)
         pack_iter = self.make_pack_iter(f)
         self.assertEntriesMatch([0, 1], entries, pack_iter)
         self.assertEqual([hex_to_sha(b1.id), hex_to_sha(b2.id)],
                          pack_iter.ext_refs())
 
     def test_bad_ext_ref_non_thin_pack(self):
-        blob, = self.store_blobs(['blob'])
+        blob, = self.store_blobs([b'blob'])
         f = BytesIO()
-        build_pack(f, [(REF_DELTA, (blob.id, 'blob1'))],
+        entries = build_pack(f, [(REF_DELTA, (blob.id, b'blob1'))],
                              store=self.store)
         pack_iter = self.make_pack_iter(f, thin=False)
         try:
@@ -1032,13 +1019,13 @@ class DeltaChainIteratorTests(TestCase):
             self.assertEqual(([blob.id],), e.args)
 
     def test_bad_ext_ref_thin_pack(self):
-        b1, b2, b3 = self.store_blobs(['foo', 'bar', 'baz'])
+        b1, b2, b3 = self.store_blobs([b'foo', b'bar', b'baz'])
         f = BytesIO()
         build_pack(f, [
-          (REF_DELTA, (1, 'foo99')),
-          (REF_DELTA, (b1.id, 'foo1')),
-          (REF_DELTA, (b2.id, 'bar2')),
-          (REF_DELTA, (b3.id, 'baz3')),
+          (REF_DELTA, (1, b'foo99')),
+          (REF_DELTA, (b1.id, b'foo1')),
+          (REF_DELTA, (b2.id, b'bar2')),
+          (REF_DELTA, (b3.id, b'baz3')),
           ], store=self.store)
         del self.store[b2.id]
         del self.store[b3.id]
@@ -1053,17 +1040,17 @@ class DeltaChainIteratorTests(TestCase):
 class DeltaEncodeSizeTests(TestCase):
 
     def test_basic(self):
-        self.assertEqual('\x00', _delta_encode_size(0))
-        self.assertEqual('\x01', _delta_encode_size(1))
-        self.assertEqual('\xfa\x01', _delta_encode_size(250))
-        self.assertEqual('\xe8\x07', _delta_encode_size(1000))
-        self.assertEqual('\xa0\x8d\x06', _delta_encode_size(100000))
+        self.assertEqual(b'\x00', _delta_encode_size(0))
+        self.assertEqual(b'\x01', _delta_encode_size(1))
+        self.assertEqual(b'\xfa\x01', _delta_encode_size(250))
+        self.assertEqual(b'\xe8\x07', _delta_encode_size(1000))
+        self.assertEqual(b'\xa0\x8d\x06', _delta_encode_size(100000))
 
 
 class EncodeCopyOperationTests(TestCase):
 
     def test_basic(self):
-        self.assertEqual('\x80', _encode_copy_operation(0, 0))
-        self.assertEqual('\x91\x01\x0a', _encode_copy_operation(1, 10))
-        self.assertEqual('\xb1\x64\xe8\x03', _encode_copy_operation(100, 1000))
-        self.assertEqual('\x93\xe8\x03\x01', _encode_copy_operation(1000, 1))
+        self.assertEqual(b'\x80', _encode_copy_operation(0, 0))
+        self.assertEqual(b'\x91\x01\x0a', _encode_copy_operation(1, 10))
+        self.assertEqual(b'\xb1\x64\xe8\x03', _encode_copy_operation(100, 1000))
+        self.assertEqual(b'\x93\xe8\x03\x01', _encode_copy_operation(1000, 1))