فهرست منبع

Previously, the word "type" was massively overloaded in objects.py. It
could refer to the numeric type of an object (obj.type or
obj._num_type), the type name of the object (obj._type or FOO_ID), or
the actual class (python type) of the object. This could get quite
confusing.

This change does the following:
-Replace obj._type and obj._num_type with type_name and type_num. (The
type property is retained for client compatibility, but is marked as
deprecated.) Change the various type maps and callers to use the
object's public members as keys.
-Add a convenience function object_class that takes either a string or
an int and dispatches to the appropriate type map.
-Rename the FOO_ID constants as _FOO_HEADER, since those constants
were previously overloaded to mean both header field names and type
names. There is some overlap, but this is intentional.
-Use isinstance for type comparisons rather than type, which is common
python practice and avoids the problematic word altogether.

Dave Borowitz 15 سال پیش
والد
کامیت
684b4ba58a
8فایلهای تغییر یافته به همراه161 افزوده شده و 148 حذف شده
  1. 12 13
      dulwich/errors.py
  2. 9 9
      dulwich/object_store.py
  3. 110 92
      dulwich/objects.py
  4. 3 3
      dulwich/pack.py
  5. 7 6
      dulwich/repo.py
  6. 3 4
      dulwich/tests/test_pack.py
  7. 14 14
      dulwich/tests/test_repository.py
  8. 3 7
      dulwich/tests/test_web.py

+ 12 - 13
dulwich/errors.py

@@ -37,40 +37,39 @@ class ChecksumMismatch(Exception):
 
 class WrongObjectException(Exception):
     """Baseclass for all the _ is not a _ exceptions on objects.
-  
+
     Do not instantiate directly.
-  
-    Subclasses should define a _type attribute that indicates what
+
+    Subclasses should define a type_name attribute that indicates what
     was expected if they were raised.
     """
-  
+
     def __init__(self, sha, *args, **kwargs):
-        string = "%s is not a %s" % (sha, self._type)
-        Exception.__init__(self, string)
+        Exception.__init__(self, "%s is not a %s" % (sha, self.type_name))
 
 
 class NotCommitError(WrongObjectException):
     """Indicates that the sha requested does not point to a commit."""
-  
-    _type = 'commit'
+
+    type_name = 'commit'
 
 
 class NotTreeError(WrongObjectException):
     """Indicates that the sha requested does not point to a tree."""
-  
-    _type = 'tree'
+
+    type_name = 'tree'
 
 
 class NotTagError(WrongObjectException):
     """Indicates that the sha requested does not point to a tag."""
 
-    _type = 'tag'
+    type_name = 'tag'
 
 
 class NotBlobError(WrongObjectException):
     """Indicates that the sha requested does not point to a blob."""
-  
-    _type = 'blob'
+
+    type_name = 'blob'
 
 
 class MissingCommitError(Exception):

+ 9 - 9
dulwich/object_store.py

@@ -92,14 +92,14 @@ class BaseObjectStore(object):
         """Obtain the raw text for an object.
 
         :param name: sha for the object.
-        :return: tuple with object type and object contents.
+        :return: tuple with numeric type and object contents.
         """
         raise NotImplementedError(self.get_raw)
 
     def __getitem__(self, sha):
         """Obtain an object by SHA1."""
-        type, uncomp = self.get_raw(sha)
-        return ShaFile.from_raw_string(type, uncomp)
+        type_num, uncomp = self.get_raw(sha)
+        return ShaFile.from_raw_string(type_num, uncomp)
 
     def __iter__(self):
         """Iterate over the SHAs that are present in this store."""
@@ -284,9 +284,9 @@ class PackBasedObjectStore(BaseObjectStore):
 
     def get_raw(self, name):
         """Obtain the raw text for an object.
-        
+
         :param name: sha for the object.
-        :return: tuple with object type and object contents.
+        :return: tuple with numeric type and object contents.
         """
         if len(name) == 40:
             sha = hex_to_sha(name)
@@ -305,7 +305,7 @@ class PackBasedObjectStore(BaseObjectStore):
             hexsha = sha_to_hex(name)
         ret = self._get_loose_object(hexsha)
         if ret is not None:
-            return ret.type, ret.as_raw_string()
+            return ret.type_num, ret.as_raw_string()
         raise KeyError(hexsha)
 
     def add_objects(self, objects):
@@ -503,9 +503,9 @@ class MemoryObjectStore(BaseObjectStore):
 
     def get_raw(self, name):
         """Obtain the raw text for an object.
-        
+
         :param name: sha for the object.
-        :return: tuple with object type and object contents.
+        :return: tuple with numeric type and object contents.
         """
         return self[name].as_raw_string()
 
@@ -621,7 +621,7 @@ def tree_lookup_path(lookup_obj, root_sha, path):
     mode = None
     for p in parts:
         obj = lookup_obj(sha)
-        if type(obj) is not Tree:
+        if not isinstance(obj, Tree):
             raise NotTreeError(sha)
         if p == '':
             continue

+ 110 - 92
dulwich/objects.py

@@ -41,23 +41,28 @@ from dulwich.misc import (
     make_sha,
     )
 
-BLOB_ID = "blob"
-TAG_ID = "tag"
-TREE_ID = "tree"
-COMMIT_ID = "commit"
-PARENT_ID = "parent"
-AUTHOR_ID = "author"
-COMMITTER_ID = "committer"
-OBJECT_ID = "object"
-TYPE_ID = "type"
-TAGGER_ID = "tagger"
-ENCODING_ID = "encoding"
+
+# Header fields for commits
+_TREE_HEADER = "tree"
+_PARENT_HEADER = "parent"
+_AUTHOR_HEADER = "author"
+_COMMITTER_HEADER = "committer"
+_ENCODING_HEADER = "encoding"
+
+
+# Header fields for objects
+_OBJECT_HEADER = "object"
+_TYPE_HEADER = "type"
+_TAG_HEADER = "tag"
+_TAGGER_HEADER = "tagger"
+
 
 S_IFGITLINK = 0160000
 
 def S_ISGITLINK(m):
     return (stat.S_IFMT(m) == S_IFGITLINK)
 
+
 def _decompress(string):
     dcomp = zlib.decompressobj()
     dcomped = dcomp.decompress(string)
@@ -89,6 +94,15 @@ def serializable_property(name, docstring=None):
     return property(get, set, doc=docstring)
 
 
+def object_class(type):
+    """Get the object class corresponding to the given type.
+
+    :param type: Either a type name string or a numeric type.
+    :return: The ShaFile subclass corresponding to the given type.
+    """
+    return _TYPE_MAP[type]
+
+
 class ShaFile(object):
     """A git SHA file."""
 
@@ -97,10 +111,10 @@ class ShaFile(object):
         """Parse a legacy object, creating it and setting object._text"""
         text = _decompress(map)
         object = None
-        for posstype in type_map.keys():
-            if text.startswith(posstype):
-                object = type_map[posstype]()
-                text = text[len(posstype):]
+        for cls in OBJECT_CLASSES:
+            if text.startswith(cls.type_name):
+                object = cls()
+                text = text[len(cls.type_name):]
                 break
         assert object is not None, "%s is not a known object type" % text[:9]
         assert text[0] == ' ', "%s is not a space" % text[0]
@@ -121,7 +135,7 @@ class ShaFile(object):
 
     def as_legacy_object(self):
         text = self.as_raw_string()
-        return zlib.compress("%s %d\0%s" % (self._type, len(text), text))
+        return zlib.compress("%s %d\0%s" % (self.type_name, len(text), text))
 
     def as_raw_chunks(self):
         if self._needs_serialization:
@@ -163,11 +177,11 @@ class ShaFile(object):
         used = 0
         byte = ord(map[used])
         used += 1
-        num_type = (byte >> 4) & 7
+        type_num = (byte >> 4) & 7
         try:
-            object = num_type_map[num_type]()
+            object = object_class(type_num)()
         except KeyError:
-            raise AssertionError("Not a known type: %d" % num_type)
+            raise AssertionError("Not a known type: %d" % type_num)
         while (byte & 0x80) != 0:
             byte = ord(map[used])
             used += 1
@@ -205,41 +219,37 @@ class ShaFile(object):
         finally:
             f.close()
 
-    @classmethod
-    def from_raw_string(cls, type, string):
+    @staticmethod
+    def from_raw_string(type_num, string):
         """Creates an object of the indicated type from the raw string given.
 
-        Type is the numeric type of an object. String is the raw uncompressed
-        contents.
+        :param type_num: The numeric type of the object.
+        :param string: The raw uncompressed contents.
         """
-        real_class = num_type_map[type]
-        obj = real_class()
-        obj.type = type
+        obj = object_class(type_num)()
         obj.set_raw_string(string)
         return obj
 
-    @classmethod
-    def from_raw_chunks(cls, type, chunks):
+    @staticmethod
+    def from_raw_chunks(type_num, chunks):
         """Creates an object of the indicated type from the raw chunks given.
 
-        Type is the numeric type of an object. Chunks is a sequence of the raw 
-        uncompressed contents.
+        :param type_num: The numeric type of the object.
+        :param chunks: An iterable of the raw uncompressed contents.
         """
-        real_class = num_type_map[type]
-        obj = real_class()
-        obj.type = type
+        obj = object_class(type_num)()
         obj.set_raw_chunks(chunks)
         return obj
 
     @classmethod
     def from_string(cls, string):
         """Create a blob from a string."""
-        shafile = cls()
-        shafile.set_raw_string(string)
-        return shafile
+        obj = cls()
+        obj.set_raw_string(string)
+        return obj
 
     def _header(self):
-        return "%s %lu\0" % (self._type, self.raw_length())
+        return "%s %lu\0" % (self.type_name, self.raw_length())
 
     def raw_length(self):
         """Returns the length of the raw string of this object."""
@@ -266,11 +276,12 @@ class ShaFile(object):
         return self.sha().hexdigest()
 
     def get_type(self):
-        return self._num_type
+        return self.type_num
 
     def set_type(self, type):
-        self._num_type = type
+        self.type_num = type
 
+    # DEPRECATED: use type_num or type_name as needed.
     type = property(get_type, set_type)
 
     def __repr__(self):
@@ -291,8 +302,8 @@ class ShaFile(object):
 class Blob(ShaFile):
     """A Git Blob object."""
 
-    _type = BLOB_ID
-    _num_type = 3
+    type_name = 'blob'
+    type_num = 3
 
     def __init__(self):
         super(Blob, self).__init__()
@@ -321,7 +332,7 @@ class Blob(ShaFile):
     @classmethod
     def from_file(cls, filename):
         blob = ShaFile.from_file(filename)
-        if blob._type != cls._type:
+        if not isinstance(blob, cls):
             raise NotBlobError(filename)
         return blob
 
@@ -329,8 +340,8 @@ class Blob(ShaFile):
 class Tag(ShaFile):
     """A Git Tag object."""
 
-    _type = TAG_ID
-    _num_type = 4
+    type_name = 'tag'
+    type_num = 4
 
     def __init__(self):
         super(Tag, self).__init__()
@@ -339,10 +350,10 @@ class Tag(ShaFile):
 
     @classmethod
     def from_file(cls, filename):
-        blob = ShaFile.from_file(filename)
-        if blob._type != cls._type:
-            raise NotBlobError(filename)
-        return blob
+        tag = ShaFile.from_file(filename)
+        if not isinstance(tag, cls):
+            raise NotTagError(filename)
+        return tag
 
     @classmethod
     def from_string(cls, string):
@@ -353,14 +364,16 @@ class Tag(ShaFile):
 
     def _serialize(self):
         chunks = []
-        chunks.append("%s %s\n" % (OBJECT_ID, self._object_sha))
-        chunks.append("%s %s\n" % (TYPE_ID, num_type_map[self._object_type]._type))
-        chunks.append("%s %s\n" % (TAG_ID, self._name))
+        chunks.append("%s %s\n" % (_OBJECT_HEADER, self._object_sha))
+        chunks.append("%s %s\n" % (_TYPE_HEADER, self._object_class.type_name))
+        chunks.append("%s %s\n" % (_TAG_HEADER, self._name))
         if self._tagger:
             if self._tag_time is None:
-                chunks.append("%s %s\n" % (TAGGER_ID, self._tagger))
+                chunks.append("%s %s\n" % (_TAGGER_HEADER, self._tagger))
             else:
-                chunks.append("%s %s %d %s\n" % (TAGGER_ID, self._tagger, self._tag_time, format_timezone(self._tag_timezone)))
+                chunks.append("%s %s %d %s\n" % (
+                  _TAGGER_HEADER, self._tagger, self._tag_time,
+                  format_timezone(self._tag_timezone)))
         chunks.append("\n") # To close headers
         chunks.append(self._message)
         return chunks
@@ -374,13 +387,13 @@ class Tag(ShaFile):
             if l == "":
                 break # empty line indicates end of headers
             (field, value) = l.split(" ", 1)
-            if field == OBJECT_ID:
+            if field == _OBJECT_HEADER:
                 self._object_sha = value
-            elif field == TYPE_ID:
-                self._object_type = type_map[value]
-            elif field == TAG_ID:
+            elif field == _TYPE_HEADER:
+                self._object_class = object_class(value)
+            elif field == _TAG_HEADER:
                 self._name = value
-            elif field == TAGGER_ID:
+            elif field == _TAGGER_HEADER:
                 try:
                     sep = value.index("> ")
                 except ValueError:
@@ -400,13 +413,16 @@ class Tag(ShaFile):
         self._message = f.read()
 
     def _get_object(self):
-        """Returns the object pointed by this tag, represented as a tuple(type, sha)"""
+        """Get the object pointed to by this tag.
+
+        :return: tuple of (object class, sha).
+        """
         self._ensure_parsed()
-        return (self._object_type, self._object_sha)
+        return (self._object_class, self._object_sha)
 
     def _set_object(self, value):
         self._ensure_parsed()
-        (self._object_type, self._object_sha) = value
+        (self._object_class, self._object_sha) = value
         self._needs_serialization = True
 
     object = property(_get_object, _set_object)
@@ -471,8 +487,8 @@ def sorted_tree_items(entries):
 class Tree(ShaFile):
     """A Git tree object"""
 
-    _type = TREE_ID
-    _num_type = 2
+    type_name = 'tree'
+    type_num = 2
 
     def __init__(self):
         super(Tree, self).__init__()
@@ -483,7 +499,7 @@ class Tree(ShaFile):
     @classmethod
     def from_file(cls, filename):
         tree = ShaFile.from_file(filename)
-        if tree._type != cls._type:
+        if not isinstance(tree, cls):
             raise NotTreeError(filename)
         return tree
 
@@ -574,8 +590,8 @@ def format_timezone(offset):
 class Commit(ShaFile):
     """A git commit object"""
 
-    _type = COMMIT_ID
-    _num_type = 1
+    type_name = 'commit'
+    type_num = 1
 
     def __init__(self):
         super(Commit, self).__init__()
@@ -588,7 +604,7 @@ class Commit(ShaFile):
     @classmethod
     def from_file(cls, filename):
         commit = ShaFile.from_file(filename)
-        if commit._type != cls._type:
+        if not isinstance(commit, cls):
             raise NotCommitError(filename)
         return commit
 
@@ -603,19 +619,19 @@ class Commit(ShaFile):
                 # Empty line indicates end of headers
                 break
             (field, value) = l.split(" ", 1)
-            if field == TREE_ID:
+            if field == _TREE_HEADER:
                 self._tree = value
-            elif field == PARENT_ID:
+            elif field == _PARENT_HEADER:
                 self._parents.append(value)
-            elif field == AUTHOR_ID:
+            elif field == _AUTHOR_HEADER:
                 self._author, timetext, timezonetext = value.rsplit(" ", 2)
                 self._author_time = int(timetext)
                 self._author_timezone = parse_timezone(timezonetext)
-            elif field == COMMITTER_ID:
+            elif field == _COMMITTER_HEADER:
                 self._committer, timetext, timezonetext = value.rsplit(" ", 2)
                 self._commit_time = int(timetext)
                 self._commit_timezone = parse_timezone(timezonetext)
-            elif field == ENCODING_ID:
+            elif field == _ENCODING_HEADER:
                 self._encoding = value
             else:
                 self._extra.append((field, value))
@@ -623,13 +639,17 @@ class Commit(ShaFile):
 
     def _serialize(self):
         chunks = []
-        chunks.append("%s %s\n" % (TREE_ID, self._tree))
+        chunks.append("%s %s\n" % (_TREE_HEADER, self._tree))
         for p in self._parents:
-            chunks.append("%s %s\n" % (PARENT_ID, p))
-        chunks.append("%s %s %s %s\n" % (AUTHOR_ID, self._author, str(self._author_time), format_timezone(self._author_timezone)))
-        chunks.append("%s %s %s %s\n" % (COMMITTER_ID, self._committer, str(self._commit_time), format_timezone(self._commit_timezone)))
+            chunks.append("%s %s\n" % (_PARENT_HEADER, p))
+        chunks.append("%s %s %s %s\n" % (
+          _AUTHOR_HEADER, self._author, str(self._author_time),
+          format_timezone(self._author_timezone)))
+        chunks.append("%s %s %s %s\n" % (
+          _COMMITTER_HEADER, self._committer, str(self._commit_time),
+          format_timezone(self._commit_timezone)))
         if self.encoding:
-            chunks.append("%s %s\n" % (ENCODING_ID, self.encoding))
+            chunks.append("%s %s\n" % (_ENCODING_HEADER, self.encoding))
         for k, v in self.extra:
             if "\n" in k or "\n" in v:
                 raise AssertionError("newline in extra data: %r -> %r" % (k, v))
@@ -685,21 +705,19 @@ class Commit(ShaFile):
         "Encoding of the commit message.")
 
 
-type_map = {
-    BLOB_ID : Blob,
-    TREE_ID : Tree,
-    COMMIT_ID : Commit,
-    TAG_ID: Tag,
-}
-
-num_type_map = {
-    0: None,
-    1: Commit,
-    2: Tree,
-    3: Blob,
-    4: Tag,
-    # 5 Is reserved for further expansion
-}
+OBJECT_CLASSES = (
+    Commit,
+    Tree,
+    Blob,
+    Tag,
+    )
+
+_TYPE_MAP = {}
+
+for cls in OBJECT_CLASSES:
+    _TYPE_MAP[cls.type_name] = cls
+    _TYPE_MAP[cls.type_num] = cls
+
 
 try:
     # Try to import C versions

+ 3 - 3
dulwich/pack.py

@@ -833,7 +833,7 @@ def write_pack_data(f, objects, num_objects, window=10):
     # This helps us find good objects to diff against us
     magic = []
     for obj, path in recency:
-        magic.append( (obj.type, path, 1, -obj.raw_length(), obj) )
+        magic.append( (obj.type_num, path, 1, -obj.raw_length(), obj) )
     magic.sort()
     # Build a map of objects and their index in magic - so we can find preceeding objects
     # to diff against
@@ -848,14 +848,14 @@ def write_pack_data(f, objects, num_objects, window=10):
     f.write(struct.pack(">L", num_objects)) # Number of objects in pack
     for o, path in recency:
         sha1 = o.sha().digest()
-        orig_t = o.type
+        orig_t = o.type_num
         raw = o.as_raw_string()
         winner = raw
         t = orig_t
         #for i in range(offs[o]-window, window):
         #    if i < 0 or i >= len(offs): continue
         #    b = magic[i][4]
-        #    if b.type != orig_t: continue
+        #    if b.type_num != orig_t: continue
         #    base = b.as_raw_string()
         #    delta = create_delta(base, raw)
         #    if len(delta) < len(winner):

+ 7 - 6
dulwich/repo.py

@@ -49,7 +49,7 @@ from dulwich.objects import (
     Tag,
     Tree,
     hex_to_sha,
-    num_type_map,
+    object_class,
     )
 import warnings
 
@@ -698,7 +698,7 @@ class BaseRepo(object):
     def _get_object(self, sha, cls):
         assert len(sha) in (20, 40)
         ret = self.get_object(sha)
-        if ret._type != cls._type:
+        if not isinstance(ret, cls):
             if cls is Commit:
                 raise NotCommitError(ret)
             elif cls is Blob:
@@ -708,7 +708,8 @@ class BaseRepo(object):
             elif cls is Tag:
                 raise NotTagError(ret)
             else:
-                raise Exception("Type invalid: %r != %r" % (ret._type, cls._type))
+                raise Exception("Type invalid: %r != %r" % (
+                  ret.type_name, cls.type_name))
         return ret
 
     def get_object(self, sha):
@@ -784,9 +785,9 @@ class BaseRepo(object):
         if cached is not None:
             return cached
         obj = self[ref]
-        obj_type = num_type_map[obj.type]
-        while obj_type == Tag:
-            obj_type, sha = obj.object
+        obj_class = object_class(obj.type_name)
+        while obj_class is Tag:
+            obj_class, sha = obj.object
             obj = self.get_object(sha)
         return obj.id
 

+ 3 - 4
dulwich/tests/test_pack.py

@@ -183,13 +183,13 @@ class TestPack(PackTests):
         """Tests random access for non-delta objects"""
         p = self.get_pack(pack1_sha)
         obj = p[a_sha]
-        self.assertEqual(obj._type, 'blob')
+        self.assertEqual(obj.type_name, 'blob')
         self.assertEqual(obj.sha().hexdigest(), a_sha)
         obj = p[tree_sha]
-        self.assertEqual(obj._type, 'tree')
+        self.assertEqual(obj.type_name, 'tree')
         self.assertEqual(obj.sha().hexdigest(), tree_sha)
         obj = p[commit_sha]
-        self.assertEqual(obj._type, 'commit')
+        self.assertEqual(obj.type_name, 'commit')
         self.assertEqual(obj.sha().hexdigest(), commit_sha)
 
     def test_copy(self):
@@ -285,4 +285,3 @@ class ZlibTests(unittest.TestCase):
     def test_simple_decompress(self):
         self.assertEquals((["tree 4ada885c9196b6b6fa08744b5862bf92896fc002\nparent None\nauthor Jelmer Vernooij <jelmer@samba.org> 1228980214 +0000\ncommitter Jelmer Vernooij <jelmer@samba.org> 1228980214 +0000\n\nProvide replacement for mmap()'s offset argument."], 158, 'Z'), 
         read_zlib_chunks(StringIO(TEST_COMP1).read, 229))
-

+ 14 - 14
dulwich/tests/test_repository.py

@@ -92,16 +92,16 @@ class RepositoryTests(unittest.TestCase):
     def test_head(self):
         r = self._repo = open_repo('a.git')
         self.assertEqual(r.head(), 'a90fa2d900a17e99b433217e988c4eb4a2e9a097')
-  
+
     def test_get_object(self):
         r = self._repo = open_repo('a.git')
         obj = r.get_object(r.head())
-        self.assertEqual(obj._type, 'commit')
-  
+        self.assertEqual(obj.type_name, 'commit')
+
     def test_get_object_non_existant(self):
         r = self._repo = open_repo('a.git')
         self.assertRaises(KeyError, r.get_object, missing_sha)
-  
+
     def test_commit(self):
         r = self._repo = open_repo('a.git')
         warnings.simplefilter("ignore", DeprecationWarning)
@@ -109,8 +109,8 @@ class RepositoryTests(unittest.TestCase):
             obj = r.commit(r.head())
         finally:
             warnings.resetwarnings()
-        self.assertEqual(obj._type, 'commit')
-  
+        self.assertEqual(obj.type_name, 'commit')
+
     def test_commit_not_commit(self):
         r = self._repo = open_repo('a.git')
         warnings.simplefilter("ignore", DeprecationWarning)
@@ -119,7 +119,7 @@ class RepositoryTests(unittest.TestCase):
                 r.commit, '4f2e6529203aa6d44b5af6e3292c837ceda003f9')
         finally:
             warnings.resetwarnings()
-  
+
     def test_tree(self):
         r = self._repo = open_repo('a.git')
         commit = r[r.head()]
@@ -128,9 +128,9 @@ class RepositoryTests(unittest.TestCase):
             tree = r.tree(commit.tree)
         finally:
             warnings.resetwarnings()
-        self.assertEqual(tree._type, 'tree')
+        self.assertEqual(tree.type_name, 'tree')
         self.assertEqual(tree.sha().hexdigest(), commit.tree)
-  
+
     def test_tree_not_tree(self):
         r = self._repo = open_repo('a.git')
         warnings.simplefilter("ignore", DeprecationWarning)
@@ -147,10 +147,10 @@ class RepositoryTests(unittest.TestCase):
             tag = r.tag(tag_sha)
         finally:
             warnings.resetwarnings()
-        self.assertEqual(tag._type, 'tag')
+        self.assertEqual(tag.type_name, 'tag')
         self.assertEqual(tag.sha().hexdigest(), tag_sha)
-        obj_type, obj_sha = tag.object
-        self.assertEqual(obj_type, objects.Commit)
+        obj_class, obj_sha = tag.object
+        self.assertEqual(obj_class, objects.Commit)
         self.assertEqual(obj_sha, r.head())
 
     def test_tag_not_tag(self):
@@ -190,9 +190,9 @@ class RepositoryTests(unittest.TestCase):
             blob = r.get_blob(blob_sha)
         finally:
             warnings.resetwarnings()
-        self.assertEqual(blob._type, 'blob')
+        self.assertEqual(blob.type_name, 'blob')
         self.assertEqual(blob.sha().hexdigest(), blob_sha)
-  
+
     def test_get_blob_notblob(self):
         r = self._repo = open_repo('a.git')
         warnings.simplefilter("ignore", DeprecationWarning)

+ 3 - 7
dulwich/tests/test_web.py

@@ -96,15 +96,11 @@ class DumbHandlersTestCase(WebTestCase):
         self._environ['QUERY_STRING'] = ''
 
         class TestTag(object):
-            type = Tag().type
-
-            def __init__(self, sha, obj_type, obj_sha):
+            def __init__(self, sha, obj_class, obj_sha):
                 self.sha = lambda: sha
-                self.object = (obj_type, obj_sha)
+                self.object = (obj_class, obj_sha)
 
         class TestBlob(object):
-            type = Blob().type
-
             def __init__(self, sha):
                 self.sha = lambda: sha
 
@@ -112,7 +108,7 @@ class DumbHandlersTestCase(WebTestCase):
         blob2 = TestBlob('222')
         blob3 = TestBlob('333')
 
-        tag1 = TestTag('aaa', TestBlob.type, '222')
+        tag1 = TestTag('aaa', Blob, '222')
 
         class TestRepo(object):
             def __init__(self, objects, peeled):