99import base
1010
1111class Tree (base .IndexObject ):
12+ """
13+ Tress represent a ordered list of Blobs and other Trees. Hence it can be
14+ accessed like a list.
15+
16+ Tree's will cache their contents after first retrieval to improve efficiency.
17+
18+ ``Tree as a list``::
19+
20+ Access a specific blob using the
21+ tree['filename'] notation.
22+
23+ You may as well access by index
24+ blob = tree[0]
25+
26+
27+ """
1228
1329 type = "tree"
14- __slots__ = "_contents "
30+ __slots__ = "_cache "
1531
1632 def __init__ (self , repo , id , mode = None , path = None ):
1733 super (Tree , self ).__init__ (repo , id , mode , path )
1834
1935 def _set_cache_ (self , attr ):
20- if attr == "_contents" :
21- # Read the tree contents.
22- self ._contents = {}
23- for line in self .repo .git .ls_tree (self .id ).splitlines ():
24- obj = self .content__from_string (self .repo , line )
25- if obj is not None :
26- self ._contents [obj .path ] = obj
36+ if attr == "_cache" :
37+ # Set the data when we need it
38+ self ._cache = self ._get_tree_cache (self .repo , self .id )
2739 else :
2840 super (Tree , self )._set_cache_ (attr )
2941
30- @staticmethod
31- def content__from_string (repo , text ):
42+ @classmethod
43+ def _get_tree_cache (cls , repo , treeish ):
44+ """
45+ Return
46+ list(object_instance, ...)
47+
48+ ``treeish``
49+ sha or ref identifying a tree
50+ """
51+ out = list ()
52+ for line in repo .git .ls_tree (treeish ).splitlines ():
53+ obj = cls .content_from_string (repo , line )
54+ if obj is not None :
55+ out .append (obj )
56+ # END if object was handled
57+ # END for each line from ls-tree
58+ return out
59+
60+
61+ @classmethod
62+ def content_from_string (cls , repo , text ):
3263 """
3364 Parse a content item and create the appropriate object
3465
@@ -40,6 +71,8 @@ def content__from_string(repo, text):
4071
4172 Returns
4273 ``git.Blob`` or ``git.Tree``
74+
75+ NOTE: Currently sub-modules are ignored !
4376 """
4477 try :
4578 mode , typ , id , path = text .expandtabs (1 ).split (" " , 3 )
@@ -51,6 +84,7 @@ def content__from_string(repo, text):
5184 elif typ == "blob" :
5285 return blob .Blob (repo , id , mode , path )
5386 elif typ == "commit" :
87+ # TODO: Return a submodule
5488 return None
5589 else :
5690 raise (TypeError , "Invalid type: %s" % typ )
@@ -67,36 +101,104 @@ def __div__(self, file):
67101 <git.Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df">
68102
69103 Returns
70- ``git.Blob`` or ``git.Tree`` or ``None`` if not found
104+ ``git.Blob`` or ``git.Tree``
105+
106+ Raise
107+ KeyError if given file or tree does not exist in tree
71108 """
72- return self . get ( file )
109+ return self [ file ]
73110
74111
75112 def __repr__ (self ):
76113 return '<git.Tree "%s">' % self .id
114+
115+ @classmethod
116+ def _iter_recursive (cls , repo , tree , cur_depth , max_depth , predicate ):
117+
118+ for obj in tree :
119+ # adjust path to be complete
120+ obj .path = os .path .join (tree .path , obj .path )
121+ if not predicate (obj ):
122+ continue
123+ yield obj
124+ if obj .type == "tree" and ( max_depth < 0 or cur_depth + 1 <= max_depth ):
125+ for recursive_obj in cls ._iter_recursive ( repo , obj , cur_depth + 1 , max_depth , predicate ):
126+ yield recursive_obj
127+ # END for each recursive object
128+ # END if we may enter recursion
129+ # END for each object
130+
131+ def traverse (self , max_depth = - 1 , predicate = lambda i : True ):
132+ """
133+ Returns
134+ Iterator to traverse the tree recursively up to the given level.
135+ The iterator returns Blob and Tree objects
136+
137+ ``max_depth``
138+
139+ if -1, the whole tree will be traversed
140+ if 0, only the first level will be traversed which is the same as
141+ the default non-recursive iterator
142+
143+ ``predicate``
144+
145+ If predicate(item) returns True, item will be returned by iterator
146+ """
147+ return self ._iter_recursive ( self .repo , self , 0 , max_depth , predicate )
148+
149+ @property
150+ def trees (self ):
151+ """
152+ Returns
153+ list(Tree, ...) list of trees directly below this tree
154+ """
155+ return [ i for i in self if i .type == "tree" ]
156+
157+ @property
158+ def blobs (self ):
159+ """
160+ Returns
161+ list(Blob, ...) list of blobs directly below this tree
162+ """
163+ return [ i for i in self if i .type == "blob" ]
77164
78- # Implement the basics of the dict protocol:
79- # directories/trees can be seen as object dicts.
80- def __getitem__ (self , key ):
81- return self ._contents [key ]
82165
166+ # List protocol
167+ def __getslice__ (self ,i ,j ):
168+ return self ._cache [i :j ]
169+
83170 def __iter__ (self ):
84- return iter (self ._contents )
85-
171+ return iter (self ._cache )
172+
86173 def __len__ (self ):
87- return len (self ._contents )
88-
89- def __contains__ (self , key ):
90- return key in self ._contents
91-
92- def get (self , key ):
93- return self ._contents .get (key )
94-
95- def items (self ):
96- return self ._contents .items ()
97-
98- def keys (self ):
99- return self ._contents .keys ()
100-
101- def values (self ):
102- return self ._contents .values ()
174+ return len (self ._cache )
175+
176+ def __getitem__ (self ,item ):
177+ if isinstance (item , int ):
178+ return self ._cache [item ]
179+
180+ if isinstance (item , basestring ):
181+ # compatability
182+ for obj in self ._cache :
183+ if obj .path == item :
184+ return obj
185+ # END for each obj
186+ raise KeyError ( "Blob or Tree named %s not found" % item )
187+ # END index is basestring
188+
189+ raise TypeError ( "Invalid index type: %r" % item )
190+
191+
192+ def __contains__ (self ,item ):
193+ if isinstance (item , base .IndexObject ):
194+ return item in self ._cache
195+
196+ # compatability
197+ for obj in self ._cache :
198+ if item == obj .path :
199+ return True
200+ # END for each item
201+ return False
202+
203+ def __reversed__ (self ):
204+ return reversed (self ._cache )
0 commit comments