Browse Source

Add python3 support to server.

Jelmer Vernooij 10 years ago
parent
commit
43a0c1b9db
3 changed files with 218 additions and 233 deletions
  1. 78 77
      dulwich/server.py
  2. 2 2
      dulwich/tests/compat/utils.py
  3. 138 154
      dulwich/tests/test_server.py

+ 78 - 77
dulwich/server.py

@@ -187,7 +187,7 @@ class Handler(object):
 
     @classmethod
     def capability_line(cls):
-        return " ".join(cls.capabilities())
+        return b" ".join(cls.capabilities())
 
     @classmethod
     def capabilities(cls):
@@ -195,7 +195,7 @@ class Handler(object):
 
     @classmethod
     def innocuous_capabilities(cls):
-        return ("include-tag", "thin-pack", "no-progress", "ofs-delta")
+        return (b"include-tag", b"thin-pack", b"no-progress", b"ofs-delta")
 
     @classmethod
     def required_capabilities(cls):
@@ -235,15 +235,15 @@ class UploadPackHandler(Handler):
 
     @classmethod
     def capabilities(cls):
-        return ("multi_ack_detailed", "multi_ack", "side-band-64k", "thin-pack",
-                "ofs-delta", "no-progress", "include-tag", "shallow")
+        return (b"multi_ack_detailed", b"multi_ack", b"side-band-64k", b"thin-pack",
+                b"ofs-delta", b"no-progress", b"include-tag", b"shallow")
 
     @classmethod
     def required_capabilities(cls):
-        return ("side-band-64k", "thin-pack", "ofs-delta")
+        return (b"side-band-64k", b"thin-pack", b"ofs-delta")
 
     def progress(self, message):
-        if self.has_capability("no-progress"):
+        if self.has_capability(b"no-progress"):
             return
         self.proto.write_sideband(2, message)
 
@@ -257,7 +257,7 @@ class UploadPackHandler(Handler):
         :return: dict of peeled_sha -> tag_sha, where tag_sha is the sha of a
             tag whose peeled value is peeled_sha.
         """
-        if not self.has_capability("include-tag"):
+        if not self.has_capability(b"include-tag"):
             return {}
         if refs is None:
             refs = self.repo.get_refs()
@@ -270,7 +270,7 @@ class UploadPackHandler(Handler):
                 # TODO: fix behavior when missing
                 return {}
         tagged = {}
-        for name, sha in refs.iteritems():
+        for name, sha in refs.items():
             peeled_sha = repo.get_peeled(name)
             if peeled_sha != sha:
                 tagged[peeled_sha] = sha
@@ -290,12 +290,12 @@ class UploadPackHandler(Handler):
         if len(objects_iter) == 0:
             return
 
-        self.progress("dul-daemon says what\n")
-        self.progress("counting objects: %d, done.\n" % len(objects_iter))
+        self.progress(b"dul-daemon says what\n")
+        self.progress(("counting objects: %d, done.\n" % len(objects_iter)).encode('ascii'))
         write_pack_objects(ProtocolFile(None, write), objects_iter)
-        self.progress("how was that, then?\n")
+        self.progress(b"how was that, then?\n")
         # we are done
-        self.proto.write("0000")
+        self.proto.write(b"0000")
 
 
 def _split_proto_line(line, allowed):
@@ -318,22 +318,22 @@ def _split_proto_line(line, allowed):
     if not line:
         fields = [None]
     else:
-        fields = line.rstrip('\n').split(' ', 1)
+        fields = line.rstrip(b'\n').split(b' ', 1)
     command = fields[0]
     if allowed is not None and command not in allowed:
         raise UnexpectedCommandError(command)
     try:
-        if len(fields) == 1 and command in ('done', None):
+        if len(fields) == 1 and command in (b'done', None):
             return (command, None)
         elif len(fields) == 2:
-            if command in ('want', 'have', 'shallow', 'unshallow'):
+            if command in (b'want', b'have', b'shallow', b'unshallow'):
                 hex_to_sha(fields[1])
                 return tuple(fields)
-            elif command == 'deepen':
+            elif command == b'deepen':
                 return command, int(fields[1])
     except (TypeError, AssertionError) as e:
         raise GitProtocolError(e)
-    raise GitProtocolError('Received invalid line from client: %s' % line)
+    raise GitProtocolError('Received invalid line from client: %r' % line)
 
 
 def _find_shallow(store, heads, depth):
@@ -381,7 +381,7 @@ def _want_satisfied(store, haves, want, earliest):
         commit = pending.popleft()
         if commit.id in haves:
             return True
-        if commit.type_name != "commit":
+        if commit.type_name != b"commit":
             # non-commit wants are assumed to be satisfied
             continue
         for parent in commit.parents:
@@ -460,17 +460,16 @@ class ProtocolGraphWalker(object):
         :param heads: a dict of refname->SHA1 to advertise
         :return: a list of SHA1s requested by the client
         """
-        values = set(heads.itervalues())
+        values = set(heads.values())
         if self.advertise_refs or not self.http_req:
-            for i, (ref, sha) in enumerate(sorted(heads.iteritems())):
-                line = "%s %s" % (sha, ref)
+            for i, (ref, sha) in enumerate(sorted(heads.items())):
+                line = sha + b' ' + ref
                 if not i:
-                    line = "%s\x00%s" % (line, self.handler.capability_line())
-                self.proto.write_pkt_line("%s\n" % line)
+                    line += b'\x00' + self.handler.capability_line()
+                self.proto.write_pkt_line(line + b'\n')
                 peeled_sha = self.get_peeled(ref)
                 if peeled_sha != sha:
-                    self.proto.write_pkt_line('%s %s^{}\n' %
-                                              (peeled_sha, ref))
+                    self.proto.write_pkt_line(peeled_sha + b' ' + ref + b'^{}\n')
 
             # i'm done..
             self.proto.write_pkt_line(None)
@@ -485,11 +484,11 @@ class ProtocolGraphWalker(object):
         line, caps = extract_want_line_capabilities(want)
         self.handler.set_client_capabilities(caps)
         self.set_ack_type(ack_type(caps))
-        allowed = ('want', 'shallow', 'deepen', None)
+        allowed = (b'want', b'shallow', b'deepen', None)
         command, sha = _split_proto_line(line, allowed)
 
         want_revs = []
-        while command == 'want':
+        while command == b'want':
             if sha not in values:
                 raise GitProtocolError(
                   'Client wants invalid object %s' % sha)
@@ -497,7 +496,7 @@ class ProtocolGraphWalker(object):
             command, sha = self.read_proto_line(allowed)
 
         self.set_wants(want_revs)
-        if command in ('shallow', 'deepen'):
+        if command in (b'shallow', b'deepen'):
             self.unread_proto_line(command, sha)
             self._handle_shallow_request(want_revs)
 
@@ -510,7 +509,9 @@ class ProtocolGraphWalker(object):
         return want_revs
 
     def unread_proto_line(self, command, value):
-        self.proto.unread_pkt_line('%s %s' % (command, value))
+        if isinstance(value, int):
+            value = str(value).encode('ascii')
+        self.proto.unread_pkt_line(command + b' ' + value)
 
     def ack(self, have_ref):
         if len(have_ref) != 40:
@@ -544,8 +545,8 @@ class ProtocolGraphWalker(object):
 
     def _handle_shallow_request(self, wants):
         while True:
-            command, val = self.read_proto_line(('deepen', 'shallow'))
-            if command == 'deepen':
+            command, val = self.read_proto_line((b'deepen', b'shallow'))
+            if command == b'deepen':
                 depth = val
                 break
             self.client_shallow.add(val)
@@ -560,19 +561,19 @@ class ProtocolGraphWalker(object):
         unshallow = self.unshallow = not_shallow & self.client_shallow
 
         for sha in sorted(new_shallow):
-            self.proto.write_pkt_line('shallow %s' % sha)
+            self.proto.write_pkt_line(b'shallow ' + sha)
         for sha in sorted(unshallow):
-            self.proto.write_pkt_line('unshallow %s' % sha)
+            self.proto.write_pkt_line(b'unshallow ' + sha)
 
         self.proto.write_pkt_line(None)
 
-    def send_ack(self, sha, ack_type=''):
+    def send_ack(self, sha, ack_type=b''):
         if ack_type:
-            ack_type = ' %s' % ack_type
-        self.proto.write_pkt_line('ACK %s%s\n' % (sha, ack_type))
+            ack_type = b' ' + ack_type
+        self.proto.write_pkt_line(b'ACK ' + sha + ack_type + b'\n')
 
     def send_nak(self):
-        self.proto.write_pkt_line('NAK\n')
+        self.proto.write_pkt_line(b'NAK\n')
 
     def set_wants(self, wants):
         self._wants = wants
@@ -595,7 +596,7 @@ class ProtocolGraphWalker(object):
         self._impl = impl_classes[ack_type](self)
 
 
-_GRAPH_WALKER_COMMANDS = ('have', 'done', None)
+_GRAPH_WALKER_COMMANDS = (b'have', b'done', None)
 
 
 class SingleAckGraphWalkerImpl(object):
@@ -612,11 +613,11 @@ class SingleAckGraphWalkerImpl(object):
 
     def next(self):
         command, sha = self.walker.read_proto_line(_GRAPH_WALKER_COMMANDS)
-        if command in (None, 'done'):
+        if command in (None, b'done'):
             if not self._sent_ack:
                 self.walker.send_nak()
             return None
-        elif command == 'have':
+        elif command == b'have':
             return sha
 
     __next__ = next
@@ -633,7 +634,7 @@ class MultiAckGraphWalkerImpl(object):
     def ack(self, have_ref):
         self._common.append(have_ref)
         if not self._found_base:
-            self.walker.send_ack(have_ref, 'continue')
+            self.walker.send_ack(have_ref, b'continue')
             if self.walker.all_wants_satisfied(self._common):
                 self._found_base = True
         # else we blind ack within next
@@ -646,7 +647,7 @@ class MultiAckGraphWalkerImpl(object):
                 # in multi-ack mode, a flush-pkt indicates the client wants to
                 # flush but more have lines are still coming
                 continue
-            elif command == 'done':
+            elif command == b'done':
                 # don't nak unless no common commits were found, even if not
                 # everything is satisfied
                 if self._common:
@@ -654,10 +655,10 @@ class MultiAckGraphWalkerImpl(object):
                 else:
                     self.walker.send_nak()
                 return None
-            elif command == 'have':
+            elif command == b'have':
                 if self._found_base:
                     # blind ack
-                    self.walker.send_ack(sha, 'continue')
+                    self.walker.send_ack(sha, b'continue')
                 return sha
 
     __next__ = next
@@ -674,10 +675,10 @@ class MultiAckDetailedGraphWalkerImpl(object):
     def ack(self, have_ref):
         self._common.append(have_ref)
         if not self._found_base:
-            self.walker.send_ack(have_ref, 'common')
+            self.walker.send_ack(have_ref, b'common')
             if self.walker.all_wants_satisfied(self._common):
                 self._found_base = True
-                self.walker.send_ack(have_ref, 'ready')
+                self.walker.send_ack(have_ref, b'ready')
         # else we blind ack within next
 
     def next(self):
@@ -688,7 +689,7 @@ class MultiAckDetailedGraphWalkerImpl(object):
                 if self.walker.http_req:
                     return None
                 continue
-            elif command == 'done':
+            elif command == b'done':
                 # don't nak unless no common commits were found, even if not
                 # everything is satisfied
                 if self._common:
@@ -696,11 +697,11 @@ class MultiAckDetailedGraphWalkerImpl(object):
                 else:
                     self.walker.send_nak()
                 return None
-            elif command == 'have':
+            elif command == b'have':
                 if self._found_base:
                     # blind ack; can happen if the client has more requests
                     # inflight
-                    self.walker.send_ack(sha, 'ready')
+                    self.walker.send_ack(sha, b'ready')
                 return sha
 
     __next__ = next
@@ -717,7 +718,7 @@ class ReceivePackHandler(Handler):
 
     @classmethod
     def capabilities(cls):
-        return ("report-status", "delete-refs", "side-band-64k")
+        return (b"report-status", b"delete-refs", b"side-band-64k")
 
     def _apply_pack(self, refs):
         all_exceptions = (IOError, OSError, ChecksumMismatch, ApplyDeltaError,
@@ -735,41 +736,41 @@ class ReceivePackHandler(Handler):
             try:
                 recv = getattr(self.proto, "recv", None)
                 self.repo.object_store.add_thin_pack(self.proto.read, recv)
-                status.append(('unpack', 'ok'))
+                status.append((b'unpack', b'ok'))
             except all_exceptions as e:
-                status.append(('unpack', str(e).replace('\n', '')))
+                status.append((b'unpack', str(e).replace('\n', '')))
                 # The pack may still have been moved in, but it may contain broken
                 # objects. We trust a later GC to clean it up.
         else:
             # The git protocol want to find a status entry related to unpack process
             # even if no pack data has been sent.
-            status.append(('unpack', 'ok'))
+            status.append((b'unpack', b'ok'))
 
         for oldsha, sha, ref in refs:
-            ref_status = 'ok'
+            ref_status = b'ok'
             try:
                 if sha == ZERO_SHA:
-                    if not 'delete-refs' in self.capabilities():
+                    if not b'delete-refs' in self.capabilities():
                         raise GitProtocolError(
                           'Attempted to delete refs without delete-refs '
                           'capability.')
                     try:
                         del self.repo.refs[ref]
                     except all_exceptions:
-                        ref_status = 'failed to delete'
+                        ref_status = b'failed to delete'
                 else:
                     try:
                         self.repo.refs[ref] = sha
                     except all_exceptions:
-                        ref_status = 'failed to write'
+                        ref_status = b'failed to write'
             except KeyError as e:
-                ref_status = 'bad ref'
+                ref_status = b'bad ref'
             status.append((ref, ref_status))
 
         return status
 
     def _report_status(self, status):
-        if self.has_capability('side-band-64k'):
+        if self.has_capability(b'side-band-64k'):
             writer = BufferedPktLineWriter(
               lambda d: self.proto.write_sideband(1, d))
             write = writer.write
@@ -782,31 +783,31 @@ class ReceivePackHandler(Handler):
             flush = lambda: None
 
         for name, msg in status:
-            if name == 'unpack':
-                write('unpack %s\n' % msg)
-            elif msg == 'ok':
-                write('ok %s\n' % name)
+            if name == b'unpack':
+                write(b'unpack ' + msg + b'\n')
+            elif msg == b'ok':
+                write(b'ok ' + name + b'\n')
             else:
-                write('ng %s %s\n' % (name, msg))
+                write(b'ng ' + name + b' ' + msg + b'\n')
         write(None)
         flush()
 
     def handle(self):
         if self.advertise_refs or not self.http_req:
-            refs = sorted(self.repo.get_refs().iteritems())
+            refs = sorted(self.repo.get_refs().items())
 
             if refs:
                 self.proto.write_pkt_line(
-                  "%s %s\x00%s\n" % (refs[0][1], refs[0][0],
-                                     self.capability_line()))
+                  refs[0][1] + b' ' + refs[0][0] + b'\0' +
+                  self.capability_line() + b'\n')
                 for i in range(1, len(refs)):
                     ref = refs[i]
-                    self.proto.write_pkt_line("%s %s\n" % (ref[1], ref[0]))
+                    self.proto.write_pkt_line(ref[1] + b' ' + ref[0] + b'\n')
             else:
-                self.proto.write_pkt_line("%s capabilities^{}\0%s" % (
-                  ZERO_SHA, self.capability_line()))
+                self.proto.write_pkt_line(ZERO_SHA + b" capabilities^{}\0" +
+                    self.capability_line())
 
-            self.proto.write("0000")
+            self.proto.write(b"0000")
             if self.advertise_refs:
                 return
 
@@ -830,14 +831,14 @@ class ReceivePackHandler(Handler):
 
         # when we have read all the pack from the client, send a status report
         # if the client asked for it
-        if self.has_capability('report-status'):
+        if self.has_capability(b'report-status'):
             self._report_status(status)
 
 
 # Default handler classes for git services.
 DEFAULT_HANDLERS = {
-  'git-upload-pack': UploadPackHandler,
-  'git-receive-pack': ReceivePackHandler,
+  b'git-upload-pack': UploadPackHandler,
+  b'git-receive-pack': ReceivePackHandler,
   }
 
 
@@ -941,7 +942,7 @@ def generate_info_refs(repo):
 def generate_objects_info_packs(repo):
     """Generate an index for for packs."""
     for pack in repo.object_store.packs:
-        yield 'P %s\n' % pack.data.filename
+        yield b'P ' + pack.data.filename + b'\n'
 
 
 def update_server_info(repo):
@@ -951,10 +952,10 @@ def update_server_info(repo):
     similar to "git update-server-info".
     """
     repo._put_named_file(os.path.join('info', 'refs'),
-        "".join(generate_info_refs(repo)))
+        b"".join(generate_info_refs(repo)))
 
     repo._put_named_file(os.path.join('objects', 'info', 'packs'),
-        "".join(generate_objects_info_packs(repo)))
+        b"".join(generate_objects_info_packs(repo)))
 
 
 if __name__ == '__main__':

+ 2 - 2
dulwich/tests/compat/utils.py

@@ -139,8 +139,8 @@ def run_git_or_fail(args, git_path=_DEFAULT_GIT, input=None, **popen_kwargs):
     returncode, stdout = run_git(args, git_path=git_path, input=input,
                                  capture_stdout=True, **popen_kwargs)
     if returncode != 0:
-        raise AssertionError("git with args %r failed with %d" % (
-            args, returncode))
+        raise AssertionError("git with args %r failed with %d: %r" % (
+            args, returncode, stdout))
     return stdout
 
 

+ 138 - 154
dulwich/tests/test_server.py

@@ -54,20 +54,18 @@ from dulwich.server import (
 from dulwich.tests import TestCase
 from dulwich.tests.utils import (
     make_commit,
-    make_object,
     make_tag,
-    skipIfPY3,
     )
 from dulwich.protocol import (
     ZERO_SHA,
     )
 
-ONE = '1' * 40
-TWO = '2' * 40
-THREE = '3' * 40
-FOUR = '4' * 40
-FIVE = '5' * 40
-SIX = '6' * 40
+ONE = b'1' * 40
+TWO = b'2' * 40
+THREE = b'3' * 40
+FOUR = b'4' * 40
+FIVE = b'5' * 40
+SIX = b'6' * 40
 
 
 class TestProto(object):
@@ -83,7 +81,7 @@ class TestProto(object):
         if self._output:
             data = self._output.pop(0)
             if data is not None:
-                return '%s\n' % data.rstrip()
+                return data.rstrip() + b'\n'
             else:
                 # flush-pkt ('0000').
                 return None
@@ -108,11 +106,11 @@ class TestGenericHandler(Handler):
 
     @classmethod
     def capabilities(cls):
-        return ('cap1', 'cap2', 'cap3')
+        return (b'cap1', b'cap2', b'cap3')
 
     @classmethod
     def required_capabilities(cls):
-        return ('cap2',)
+        return (b'cap2',)
 
 
 class HandlerTestCase(TestCase):
@@ -128,81 +126,80 @@ class HandlerTestCase(TestCase):
             self.fail(e)
 
     def test_capability_line(self):
-        self.assertEqual('cap1 cap2 cap3', self._handler.capability_line())
+        self.assertEqual(b'cap1 cap2 cap3', self._handler.capability_line())
 
     def test_set_client_capabilities(self):
         set_caps = self._handler.set_client_capabilities
-        self.assertSucceeds(set_caps, ['cap2'])
-        self.assertSucceeds(set_caps, ['cap1', 'cap2'])
+        self.assertSucceeds(set_caps, [b'cap2'])
+        self.assertSucceeds(set_caps, [b'cap1', b'cap2'])
 
         # different order
-        self.assertSucceeds(set_caps, ['cap3', 'cap1', 'cap2'])
+        self.assertSucceeds(set_caps, [b'cap3', b'cap1', b'cap2'])
 
         # error cases
-        self.assertRaises(GitProtocolError, set_caps, ['capxxx', 'cap2'])
-        self.assertRaises(GitProtocolError, set_caps, ['cap1', 'cap3'])
+        self.assertRaises(GitProtocolError, set_caps, [b'capxxx', b'cap2'])
+        self.assertRaises(GitProtocolError, set_caps, [b'cap1', b'cap3'])
 
         # ignore innocuous but unknown capabilities
-        self.assertRaises(GitProtocolError, set_caps, ['cap2', 'ignoreme'])
-        self.assertFalse('ignoreme' in self._handler.capabilities())
-        self._handler.innocuous_capabilities = lambda: ('ignoreme',)
-        self.assertSucceeds(set_caps, ['cap2', 'ignoreme'])
+        self.assertRaises(GitProtocolError, set_caps, [b'cap2', b'ignoreme'])
+        self.assertFalse(b'ignoreme' in self._handler.capabilities())
+        self._handler.innocuous_capabilities = lambda: (b'ignoreme',)
+        self.assertSucceeds(set_caps, [b'cap2', b'ignoreme'])
 
     def test_has_capability(self):
-        self.assertRaises(GitProtocolError, self._handler.has_capability, 'cap')
+        self.assertRaises(GitProtocolError, self._handler.has_capability, b'cap')
         caps = self._handler.capabilities()
         self._handler.set_client_capabilities(caps)
         for cap in caps:
             self.assertTrue(self._handler.has_capability(cap))
-        self.assertFalse(self._handler.has_capability('capxxx'))
+        self.assertFalse(self._handler.has_capability(b'capxxx'))
 
 
-@skipIfPY3
 class UploadPackHandlerTestCase(TestCase):
 
     def setUp(self):
         super(UploadPackHandlerTestCase, self).setUp()
         self._repo = MemoryRepo.init_bare([], {})
-        backend = DictBackend({'/': self._repo})
+        backend = DictBackend({b'/': self._repo})
         self._handler = UploadPackHandler(
-          backend, ['/', 'host=lolcathost'], TestProto())
+          backend, [b'/', b'host=lolcathost'], TestProto())
 
     def test_progress(self):
         caps = self._handler.required_capabilities()
         self._handler.set_client_capabilities(caps)
-        self._handler.progress('first message')
-        self._handler.progress('second message')
-        self.assertEqual('first message',
+        self._handler.progress(b'first message')
+        self._handler.progress(b'second message')
+        self.assertEqual(b'first message',
                          self._handler.proto.get_received_line(2))
-        self.assertEqual('second message',
+        self.assertEqual(b'second message',
                          self._handler.proto.get_received_line(2))
         self.assertRaises(IndexError, self._handler.proto.get_received_line, 2)
 
     def test_no_progress(self):
-        caps = list(self._handler.required_capabilities()) + ['no-progress']
+        caps = list(self._handler.required_capabilities()) + [b'no-progress']
         self._handler.set_client_capabilities(caps)
-        self._handler.progress('first message')
-        self._handler.progress('second message')
+        self._handler.progress(b'first message')
+        self._handler.progress(b'second message')
         self.assertRaises(IndexError, self._handler.proto.get_received_line, 2)
 
     def test_get_tagged(self):
         refs = {
-            'refs/tags/tag1': ONE,
-            'refs/tags/tag2': TWO,
-            'refs/heads/master': FOUR,  # not a tag, no peeled value
+            b'refs/tags/tag1': ONE,
+            b'refs/tags/tag2': TWO,
+            b'refs/heads/master': FOUR,  # not a tag, no peeled value
             }
         # repo needs to peel this object
         self._repo.object_store.add_object(make_commit(id=FOUR))
         self._repo.refs._update(refs)
         peeled = {
-            'refs/tags/tag1': '1234' * 10,
-            'refs/tags/tag2': '5678' * 10,
+            b'refs/tags/tag1': b'1234' * 10,
+            b'refs/tags/tag2': b'5678' * 10,
             }
         self._repo.refs._update_peeled(peeled)
 
-        caps = list(self._handler.required_capabilities()) + ['include-tag']
+        caps = list(self._handler.required_capabilities()) + [b'include-tag']
         self._handler.set_client_capabilities(caps)
-        self.assertEqual({'1234' * 10: ONE, '5678' * 10: TWO},
+        self.assertEqual({b'1234' * 10: ONE, b'5678' * 10: TWO},
                           self._handler.get_tagged(refs, repo=self._repo))
 
         # non-include-tag case
@@ -211,7 +208,6 @@ class UploadPackHandlerTestCase(TestCase):
         self.assertEqual({}, self._handler.get_tagged(refs, repo=self._repo))
 
 
-@skipIfPY3
 class FindShallowTests(TestCase):
 
     def setUp(self):
@@ -222,7 +218,7 @@ class FindShallowTests(TestCase):
         self._store.add_object(commit)
         return commit
 
-    def make_linear_commits(self, n, message=''):
+    def make_linear_commits(self, n, message=b''):
         commits = []
         parents = []
         for _ in range(n):
@@ -246,9 +242,9 @@ class FindShallowTests(TestCase):
                          _find_shallow(self._store, [c3.id], 3))
 
     def test_multiple_independent(self):
-        a = self.make_linear_commits(2, message='a')
-        b = self.make_linear_commits(2, message='b')
-        c = self.make_linear_commits(2, message='c')
+        a = self.make_linear_commits(2, message=b'a')
+        b = self.make_linear_commits(2, message=b'b')
+        c = self.make_linear_commits(2, message=b'c')
         heads = [a[1].id, b[1].id, c[1].id]
 
         self.assertEqual((set([a[0].id, b[0].id, c[0].id]), set(heads)),
@@ -277,7 +273,7 @@ class FindShallowTests(TestCase):
 
     def test_tag(self):
         c1, c2 = self.make_linear_commits(2)
-        tag = make_tag(c2, name='tag')
+        tag = make_tag(c2, name=b'tag')
         self._store.add_object(tag)
 
         self.assertEqual((set([c1.id]), set([c2.id])),
@@ -289,37 +285,35 @@ class TestUploadPackHandler(UploadPackHandler):
     def required_capabilities(self):
         return ()
 
-@skipIfPY3
 class ReceivePackHandlerTestCase(TestCase):
 
     def setUp(self):
         super(ReceivePackHandlerTestCase, self).setUp()
         self._repo = MemoryRepo.init_bare([], {})
-        backend = DictBackend({'/': self._repo})
+        backend = DictBackend({b'/': self._repo})
         self._handler = ReceivePackHandler(
-          backend, ['/', 'host=lolcathost'], TestProto())
+          backend, [b'/', b'host=lolcathost'], TestProto())
 
     def test_apply_pack_del_ref(self):
         refs = {
-            'refs/heads/master': TWO,
-            'refs/heads/fake-branch': ONE}
+            b'refs/heads/master': TWO,
+            b'refs/heads/fake-branch': ONE}
         self._repo.refs._update(refs)
-        update_refs = [[ONE, ZERO_SHA, 'refs/heads/fake-branch'], ]
+        update_refs = [[ONE, ZERO_SHA, b'refs/heads/fake-branch'], ]
         status = self._handler._apply_pack(update_refs)
-        self.assertEqual(status[0][0], 'unpack')
-        self.assertEqual(status[0][1], 'ok')
-        self.assertEqual(status[1][0], 'refs/heads/fake-branch')
-        self.assertEqual(status[1][1], 'ok')
+        self.assertEqual(status[0][0], b'unpack')
+        self.assertEqual(status[0][1], b'ok')
+        self.assertEqual(status[1][0], b'refs/heads/fake-branch')
+        self.assertEqual(status[1][1], b'ok')
 
 
-@skipIfPY3
 class ProtocolGraphWalkerEmptyTestCase(TestCase):
     def setUp(self):
         super(ProtocolGraphWalkerEmptyTestCase, self).setUp()
         self._repo = MemoryRepo.init_bare([], {})
-        backend = DictBackend({'/': self._repo})
+        backend = DictBackend({b'/': self._repo})
         self._walker = ProtocolGraphWalker(
-            TestUploadPackHandler(backend, ['/', 'host=lolcats'], TestProto()),
+            TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()),
             self._repo.object_store, self._repo.get_peeled)
 
     def test_empty_repository(self):
@@ -334,7 +328,6 @@ class ProtocolGraphWalkerEmptyTestCase(TestCase):
 
 
 
-@skipIfPY3
 class ProtocolGraphWalkerTestCase(TestCase):
 
     def setUp(self):
@@ -351,9 +344,9 @@ class ProtocolGraphWalkerTestCase(TestCase):
           make_commit(id=FIVE, parents=[THREE], commit_time=555),
           ]
         self._repo = MemoryRepo.init_bare(commits, {})
-        backend = DictBackend({'/': self._repo})
+        backend = DictBackend({b'/': self._repo})
         self._walker = ProtocolGraphWalker(
-            TestUploadPackHandler(backend, ['/', 'host=lolcats'], TestProto()),
+            TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()),
             self._repo.object_store, self._repo.get_peeled)
 
     def test_all_wants_satisfied_no_haves(self):
@@ -390,20 +383,20 @@ class ProtocolGraphWalkerTestCase(TestCase):
         self.assertTrue(self._walker.all_wants_satisfied([TWO, THREE]))
 
     def test_split_proto_line(self):
-        allowed = ('want', 'done', None)
-        self.assertEqual(('want', ONE),
-                          _split_proto_line('want %s\n' % ONE, allowed))
-        self.assertEqual(('want', TWO),
-                          _split_proto_line('want %s\n' % TWO, allowed))
+        allowed = (b'want', b'done', None)
+        self.assertEqual((b'want', ONE),
+                          _split_proto_line(b'want ' + ONE + b'\n', allowed))
+        self.assertEqual((b'want', TWO),
+                          _split_proto_line(b'want ' + TWO + b'\n', allowed))
         self.assertRaises(GitProtocolError, _split_proto_line,
-                          'want xxxx\n', allowed)
+                          b'want xxxx\n', allowed)
         self.assertRaises(UnexpectedCommandError, _split_proto_line,
-                          'have %s\n' % THREE, allowed)
+                          b'have ' + THREE + b'\n', allowed)
         self.assertRaises(GitProtocolError, _split_proto_line,
-                          'foo %s\n' % FOUR, allowed)
-        self.assertRaises(GitProtocolError, _split_proto_line, 'bar', allowed)
-        self.assertEqual(('done', None), _split_proto_line('done\n', allowed))
-        self.assertEqual((None, None), _split_proto_line('', allowed))
+                          b'foo ' + FOUR + b'\n', allowed)
+        self.assertRaises(GitProtocolError, _split_proto_line, b'bar', allowed)
+        self.assertEqual((b'done', None), _split_proto_line(b'done\n', allowed))
+        self.assertEqual((None, None), _split_proto_line(b'', allowed))
 
     def test_determine_wants(self):
         self._walker.proto.set_output([None])
@@ -411,14 +404,14 @@ class ProtocolGraphWalkerTestCase(TestCase):
         self.assertEqual(None, self._walker.proto.get_received_line())
 
         self._walker.proto.set_output([
-          'want %s multi_ack' % ONE,
-          'want %s' % TWO,
+          b'want ' + ONE + b' multi_ack',
+          b'want ' + TWO,
           None,
           ])
         heads = {
-          'refs/heads/ref1': ONE,
-          'refs/heads/ref2': TWO,
-          'refs/heads/ref3': THREE,
+          b'refs/heads/ref1': ONE,
+          b'refs/heads/ref2': TWO,
+          b'refs/heads/ref3': THREE,
           }
         self._repo.refs._update(heads)
         self.assertEqual([ONE, TWO], self._walker.determine_wants(heads))
@@ -427,29 +420,29 @@ class ProtocolGraphWalkerTestCase(TestCase):
         self.assertEqual([], self._walker.determine_wants(heads))
         self._walker.advertise_refs = False
 
-        self._walker.proto.set_output(['want %s multi_ack' % FOUR, None])
+        self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None])
         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
         self._walker.proto.set_output([None])
         self.assertEqual([], self._walker.determine_wants(heads))
 
-        self._walker.proto.set_output(['want %s multi_ack' % ONE, 'foo', None])
+        self._walker.proto.set_output([b'want ' + ONE + b' multi_ack', b'foo', None])
         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
-        self._walker.proto.set_output(['want %s multi_ack' % FOUR, None])
+        self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None])
         self.assertRaises(GitProtocolError, self._walker.determine_wants, heads)
 
     def test_determine_wants_advertisement(self):
         self._walker.proto.set_output([None])
         # advertise branch tips plus tag
         heads = {
-          'refs/heads/ref4': FOUR,
-          'refs/heads/ref5': FIVE,
-          'refs/heads/tag6': SIX,
+          b'refs/heads/ref4': FOUR,
+          b'refs/heads/ref5': FIVE,
+          b'refs/heads/tag6': SIX,
           }
         self._repo.refs._update(heads)
         self._repo.refs._update_peeled(heads)
-        self._repo.refs._update_peeled({'refs/heads/tag6': FIVE})
+        self._repo.refs._update_peeled({b'refs/heads/tag6': FIVE})
         self._walker.determine_wants(heads)
         lines = []
         while True:
@@ -457,21 +450,21 @@ class ProtocolGraphWalkerTestCase(TestCase):
             if line is None:
                 break
             # strip capabilities list if present
-            if '\x00' in line:
-                line = line[:line.index('\x00')]
+            if b'\x00' in line:
+                line = line[:line.index(b'\x00')]
             lines.append(line.rstrip())
 
         self.assertEqual([
-          '%s refs/heads/ref4' % FOUR,
-          '%s refs/heads/ref5' % FIVE,
-          '%s refs/heads/tag6^{}' % FIVE,
-          '%s refs/heads/tag6' % SIX,
+          FOUR + b' refs/heads/ref4',
+          FIVE + b' refs/heads/ref5',
+          FIVE + b' refs/heads/tag6^{}',
+          SIX + b' refs/heads/tag6',
           ], sorted(lines))
 
         # ensure peeled tag was advertised immediately following tag
         for i, line in enumerate(lines):
-            if line.endswith(' refs/heads/tag6'):
-                self.assertEqual('%s refs/heads/tag6^{}' % FIVE, lines[i+1])
+            if line.endswith(b' refs/heads/tag6'):
+                self.assertEqual(FIVE + b' refs/heads/tag6^{}', lines[i+1])
 
     # TODO: test commit time cutoff
 
@@ -484,18 +477,18 @@ class ProtocolGraphWalkerTestCase(TestCase):
           expected, list(iter(self._walker.proto.get_received_line, None)))
 
     def test_handle_shallow_request_no_client_shallows(self):
-        self._handle_shallow_request(['deepen 1\n'], [FOUR, FIVE])
+        self._handle_shallow_request([b'deepen 1\n'], [FOUR, FIVE])
         self.assertEqual(set([TWO, THREE]), self._walker.shallow)
         self.assertReceived([
-          'shallow %s' % TWO,
-          'shallow %s' % THREE,
+          b'shallow ' + TWO,
+          b'shallow ' + THREE,
           ])
 
     def test_handle_shallow_request_no_new_shallows(self):
         lines = [
-          'shallow %s\n' % TWO,
-          'shallow %s\n' % THREE,
-          'deepen 1\n',
+          b'shallow ' + TWO + b'\n',
+          b'shallow ' + THREE + b'\n',
+          b'deepen 1\n',
           ]
         self._handle_shallow_request(lines, [FOUR, FIVE])
         self.assertEqual(set([TWO, THREE]), self._walker.shallow)
@@ -503,19 +496,18 @@ class ProtocolGraphWalkerTestCase(TestCase):
 
     def test_handle_shallow_request_unshallows(self):
         lines = [
-          'shallow %s\n' % TWO,
-          'deepen 2\n',
+          b'shallow ' + TWO + b'\n',
+          b'deepen 2\n',
           ]
         self._handle_shallow_request(lines, [FOUR, FIVE])
         self.assertEqual(set([ONE]), self._walker.shallow)
         self.assertReceived([
-          'shallow %s' % ONE,
-          'unshallow %s' % TWO,
+          b'shallow ' + ONE,
+          b'unshallow ' + TWO,
           # THREE is unshallow but was is not shallow in the client
           ])
 
 
-@skipIfPY3
 class TestProtocolGraphWalker(object):
 
     def __init__(self):
@@ -531,11 +523,11 @@ class TestProtocolGraphWalker(object):
             assert command in allowed
         return command, sha
 
-    def send_ack(self, sha, ack_type=''):
+    def send_ack(self, sha, ack_type=b''):
         self.acks.append((sha, ack_type))
 
     def send_nak(self):
-        self.acks.append((None, 'nak'))
+        self.acks.append((None, b'nak'))
 
     def all_wants_satisfied(self, haves):
         return self.done
@@ -546,7 +538,6 @@ class TestProtocolGraphWalker(object):
         return self.acks.pop(0)
 
 
-@skipIfPY3
 class AckGraphWalkerImplTestCase(TestCase):
     """Base setup and asserts for AckGraphWalker tests."""
 
@@ -554,10 +545,10 @@ class AckGraphWalkerImplTestCase(TestCase):
         super(AckGraphWalkerImplTestCase, self).setUp()
         self._walker = TestProtocolGraphWalker()
         self._walker.lines = [
-          ('have', TWO),
-          ('have', ONE),
-          ('have', THREE),
-          ('done', None),
+          (b'have', TWO),
+          (b'have', ONE),
+          (b'have', THREE),
+          (b'done', None),
           ]
         self._impl = self.impl_cls(self._walker)
 
@@ -569,17 +560,16 @@ class AckGraphWalkerImplTestCase(TestCase):
             self.assertEqual((sha, ack_type), self._walker.pop_ack())
         self.assertNoAck()
 
-    def assertAck(self, sha, ack_type=''):
+    def assertAck(self, sha, ack_type=b''):
         self.assertAcks([(sha, ack_type)])
 
     def assertNak(self):
-        self.assertAck(None, 'nak')
+        self.assertAck(None, b'nak')
 
     def assertNextEquals(self, sha):
         self.assertEqual(sha, next(self._impl))
 
 
-@skipIfPY3
 class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
     impl_cls = SingleAckGraphWalkerImpl
@@ -648,7 +638,6 @@ class SingleAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
         self.assertNak()
 
 
-@skipIfPY3
 class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
     impl_cls = MultiAckGraphWalkerImpl
@@ -660,11 +649,11 @@ class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
         self.assertNextEquals(ONE)
         self._walker.done = True
         self._impl.ack(ONE)
-        self.assertAck(ONE, 'continue')
+        self.assertAck(ONE, b'continue')
 
         self.assertNextEquals(THREE)
         self._impl.ack(THREE)
-        self.assertAck(THREE, 'continue')
+        self.assertAck(THREE, b'continue')
 
         self.assertNextEquals(None)
         self.assertAck(THREE)
@@ -675,7 +664,7 @@ class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
         self.assertNextEquals(ONE)
         self._impl.ack(ONE)
-        self.assertAck(ONE, 'continue')
+        self.assertAck(ONE, b'continue')
 
         self.assertNextEquals(THREE)
         self.assertNoAck()
@@ -686,11 +675,11 @@ class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
     def test_multi_ack_flush(self):
         self._walker.lines = [
-          ('have', TWO),
+          (b'have', TWO),
           (None, None),
-          ('have', ONE),
-          ('have', THREE),
-          ('done', None),
+          (b'have', ONE),
+          (b'have', THREE),
+          (b'done', None),
           ]
         self.assertNextEquals(TWO)
         self.assertNoAck()
@@ -700,11 +689,11 @@ class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
         self._walker.done = True
         self._impl.ack(ONE)
-        self.assertAck(ONE, 'continue')
+        self.assertAck(ONE, b'continue')
 
         self.assertNextEquals(THREE)
         self._impl.ack(THREE)
-        self.assertAck(THREE, 'continue')
+        self.assertAck(THREE, b'continue')
 
         self.assertNextEquals(None)
         self.assertAck(THREE)
@@ -723,7 +712,6 @@ class MultiAckGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
         self.assertNak()
 
 
-@skipIfPY3
 class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
     impl_cls = MultiAckDetailedGraphWalkerImpl
@@ -735,11 +723,11 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
         self.assertNextEquals(ONE)
         self._walker.done = True
         self._impl.ack(ONE)
-        self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
+        self.assertAcks([(ONE, b'common'), (ONE, b'ready')])
 
         self.assertNextEquals(THREE)
         self._impl.ack(THREE)
-        self.assertAck(THREE, 'ready')
+        self.assertAck(THREE, b'ready')
 
         self.assertNextEquals(None)
         self.assertAck(THREE)
@@ -750,7 +738,7 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
         self.assertNextEquals(ONE)
         self._impl.ack(ONE)
-        self.assertAck(ONE, 'common')
+        self.assertAck(ONE, b'common')
 
         self.assertNextEquals(THREE)
         self.assertNoAck()
@@ -762,11 +750,11 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
     def test_multi_ack_flush(self):
         # same as ack test but contains a flush-pkt in the middle
         self._walker.lines = [
-          ('have', TWO),
+          (b'have', TWO),
           (None, None),
-          ('have', ONE),
-          ('have', THREE),
-          ('done', None),
+          (b'have', ONE),
+          (b'have', THREE),
+          (b'done', None),
           ]
         self.assertNextEquals(TWO)
         self.assertNoAck()
@@ -776,11 +764,11 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
 
         self._walker.done = True
         self._impl.ack(ONE)
-        self.assertAcks([(ONE, 'common'), (ONE, 'ready')])
+        self.assertAcks([(ONE, b'common'), (ONE, b'ready')])
 
         self.assertNextEquals(THREE)
         self._impl.ack(THREE)
-        self.assertAck(THREE, 'ready')
+        self.assertAck(THREE, b'ready')
 
         self.assertNextEquals(None)
         self.assertAck(THREE)
@@ -801,11 +789,11 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
     def test_multi_ack_nak_flush(self):
         # same as nak test but contains a flush-pkt in the middle
         self._walker.lines = [
-          ('have', TWO),
+          (b'have', TWO),
           (None, None),
-          ('have', ONE),
-          ('have', THREE),
-          ('done', None),
+          (b'have', ONE),
+          (b'have', THREE),
+          (b'done', None),
           ]
         self.assertNextEquals(TWO)
         self.assertNoAck()
@@ -837,7 +825,6 @@ class MultiAckDetailedGraphWalkerImplTestCase(AckGraphWalkerImplTestCase):
         self.assertNak()
 
 
-@skipIfPY3
 class FileSystemBackendTests(TestCase):
     """Tests for FileSystemBackend."""
 
@@ -866,25 +853,23 @@ class FileSystemBackendTests(TestCase):
                           lambda: backend.open_repository('/ups'))
 
 
-@skipIfPY3
 class DictBackendTests(TestCase):
     """Tests for DictBackend."""
 
     def test_nonexistant(self):
         repo = MemoryRepo.init_bare([], {})
-        backend = DictBackend({'/': repo})
+        backend = DictBackend({b'/': repo})
         self.assertRaises(NotGitRepository,
             backend.open_repository, "/does/not/exist/unless/foo")
 
     def test_bad_repo_path(self):
         repo = MemoryRepo.init_bare([], {})
-        backend = DictBackend({'/': repo})
+        backend = DictBackend({b'/': repo})
 
         self.assertRaises(NotGitRepository,
                           lambda: backend.open_repository('/ups'))
 
 
-@skipIfPY3
 class ServeCommandTests(TestCase):
     """Tests for serve_command."""
 
@@ -893,24 +878,23 @@ class ServeCommandTests(TestCase):
         self.backend = DictBackend({})
 
     def serve_command(self, handler_cls, args, inf, outf):
-        return serve_command(handler_cls, ["test"] + args, backend=self.backend,
+        return serve_command(handler_cls, [b"test"] + args, backend=self.backend,
             inf=inf, outf=outf)
 
     def test_receive_pack(self):
         commit = make_commit(id=ONE, parents=[], commit_time=111)
-        self.backend.repos["/"] = MemoryRepo.init_bare(
-            [commit], {"refs/heads/master": commit.id})
+        self.backend.repos[b"/"] = MemoryRepo.init_bare(
+            [commit], {b"refs/heads/master": commit.id})
         outf = BytesIO()
-        exitcode = self.serve_command(ReceivePackHandler, ["/"], BytesIO("0000"), outf)
+        exitcode = self.serve_command(ReceivePackHandler, [b"/"], BytesIO(b"0000"), outf)
         outlines = outf.getvalue().splitlines()
         self.assertEqual(2, len(outlines))
-        self.assertEqual("1111111111111111111111111111111111111111 refs/heads/master",
-            outlines[0][4:].split("\x00")[0])
-        self.assertEqual("0000", outlines[-1])
+        self.assertEqual(b"1111111111111111111111111111111111111111 refs/heads/master",
+            outlines[0][4:].split(b"\x00")[0])
+        self.assertEqual(b"0000", outlines[-1])
         self.assertEqual(0, exitcode)
 
 
-@skipIfPY3
 class UpdateServerInfoTests(TestCase):
     """Tests for update_server_info."""
 
@@ -928,9 +912,9 @@ class UpdateServerInfoTests(TestCase):
 
     def test_simple(self):
         commit_id = self.repo.do_commit(
-            message="foo",
-            committer="Joe Example <joe@example.com>",
-            ref="refs/heads/foo")
+            message=b"foo",
+            committer=b"Joe Example <joe@example.com>",
+            ref=b"refs/heads/foo")
         update_server_info(self.repo)
         with open(os.path.join(self.path, ".git", "info", "refs"), 'rb') as f:
             self.assertEqual(f.read(), commit_id + b'\trefs/heads/foo\n')