Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6e5aae2

Browse files
committed
Initial interface including some of the implementation of the RefLog. TestCase scetched out for now
tests: Added tests to verify that objects don't have a dict. Previously, due to a missing __slots__ member in Serializable, most objects would indeed have a dict, although the opposite was intended
1 parent739fa14 commit6e5aae2

File tree

8 files changed

+255
-48
lines changed

8 files changed

+255
-48
lines changed

‎objects/util.py

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def get_user_id():
7171

7272
defutctz_to_altz(utctz):
7373
"""we convert utctz to the timezone in seconds, it is the format time.altzone
74-
returns. Git stores it as UTCtimezon which has the opposite sign as well,
74+
returns. Git stores it as UTCtimezone which has the opposite sign as well,
7575
which explains the -1 * ( that was made explicit here )
7676
:param utctz: git utc timezone string, i.e. +0200"""
7777
return-1*int(float(utctz)/100*3600)
@@ -195,53 +195,55 @@ def parse_actor_and_date(line):
195195
#{ Classes
196196

197197
classActor(object):
198-
"""Actors hold information about a person acting on the repository. They
199-
can be committers and authors or anything with a name and an email as
200-
mentioned in the git log entries."""
201-
# precompiled regex
202-
name_only_regex=re.compile(r'<(.+)>' )
203-
name_email_regex=re.compile(r'(.*) <(.+?)>' )
204-
205-
def__init__(self,name,email):
206-
self.name=name
207-
self.email=email
198+
"""Actors hold information about a person acting on the repository. They
199+
can be committers and authors or anything with a name and an email as
200+
mentioned in the git log entries."""
201+
# precompiled regex
202+
name_only_regex=re.compile(r'<(.+)>' )
203+
name_email_regex=re.compile(r'(.*) <(.+?)>' )
204+
205+
__slots__= ('name','email')
206+
207+
def__init__(self,name,email):
208+
self.name=name
209+
self.email=email
208210

209-
def__eq__(self,other):
210-
returnself.name==other.nameandself.email==other.email
211-
212-
def__ne__(self,other):
213-
returnnot (self==other)
214-
215-
def__hash__(self):
216-
returnhash((self.name,self.email))
211+
def__eq__(self,other):
212+
returnself.name==other.nameandself.email==other.email
213+
214+
def__ne__(self,other):
215+
returnnot (self==other)
216+
217+
def__hash__(self):
218+
returnhash((self.name,self.email))
217219

218-
def__str__(self):
219-
returnself.name
220+
def__str__(self):
221+
returnself.name
220222

221-
def__repr__(self):
222-
return'<git.Actor "%s <%s>">'% (self.name,self.email)
223+
def__repr__(self):
224+
return'<git.Actor "%s <%s>">'% (self.name,self.email)
223225

224-
@classmethod
225-
def_from_string(cls,string):
226-
"""Create an Actor from a string.
226+
@classmethod
227+
def_from_string(cls,string):
228+
"""Create an Actor from a string.
227229
:param string: is the string, which is expected to be in regular git format
228230
229231
John Doe <jdoe@example.com>
230232
231233
:return: Actor """
232-
m=cls.name_email_regex.search(string)
233-
ifm:
234-
name,email=m.groups()
235-
returnActor(name,email)
236-
else:
237-
m=cls.name_only_regex.search(string)
238-
ifm:
239-
returnActor(m.group(1),None)
240-
else:
241-
# assume best and use the whole string as name
242-
returnActor(string,None)
243-
# END special case name
244-
# END handle name/email matching
234+
m=cls.name_email_regex.search(string)
235+
ifm:
236+
name,email=m.groups()
237+
returnActor(name,email)
238+
else:
239+
m=cls.name_only_regex.search(string)
240+
ifm:
241+
returnActor(m.group(1),None)
242+
else:
243+
# assume best and use the whole string as name
244+
returnActor(string,None)
245+
# END special case name
246+
# END handle name/email matching
245247

246248

247249
classProcessStreamAdapter(object):
@@ -359,6 +361,7 @@ def addToStack( stack, item, branch_first, depth ):
359361

360362
classSerializable(object):
361363
"""Defines methods to serialize and deserialize objects from and into a data stream"""
364+
__slots__=tuple()
362365

363366
def_serialize(self,stream):
364367
"""Serialize the data of this object into the given data stream

‎refs/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
foritemin (HEAD,Head,RemoteReference,TagReference,Reference,SymbolicReference):
1717
setattr(symbolic,item.__name__,item)
1818
del(symbolic)
19-
# git.objects.Commit -> symbolic
20-
# git.config.SectionConstraint -> head
19+
20+
21+
fromlogimport*

‎refs/log.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
fromheadimportHead
2+
fromgit.utilimportjoin_path
3+
fromgitdb.utilimport (
4+
join,
5+
file_contents_ro_filepath
6+
)
7+
8+
fromgit.objects.utilimport (
9+
Actor,
10+
parse_actor_and_date,
11+
Serializable,
12+
utctz_to_altz,
13+
altz_to_utctz_str,
14+
)
15+
16+
importos
17+
18+
19+
__all__= ["RefLog","RefLogEntry"]
20+
21+
22+
classRefLogEntry(tuple):
23+
"""Named tuple allowing easy access to the revlog data fields"""
24+
_fmt="%s %s %s <%s> %i %s\t%s"
25+
__slots__=tuple()
26+
27+
def__repr__(self):
28+
"""Representation of ourselves in git reflog format"""
29+
act=self.actor
30+
time=self.time
31+
returnself._fmt% (self.oldhexsha,self.newhexsha,act.name,act.email,
32+
time[0],altz_to_utctz_str(time[1]),self.message)
33+
34+
@property
35+
defoldhexsha(self):
36+
"""The hexsha to the commit the ref pointed to before the change"""
37+
returnself[0]
38+
39+
@property
40+
defnewhexsha(self):
41+
"""The hexsha to the commit the ref now points to, after the change"""
42+
returnself[1]
43+
44+
@property
45+
defactor(self):
46+
"""Actor instance, providing access"""
47+
returnself[2]
48+
49+
@property
50+
deftime(self):
51+
"""time as tuple:
52+
53+
* [0] = int(time)
54+
* [1] = int(timezone_offset) in time.altzone format """
55+
returnself[3]
56+
57+
@property
58+
defmessage(self):
59+
"""Message describing the operation that acted on the reference"""
60+
returnself[4]
61+
62+
@classmethod
63+
defnew(self,oldhexsha,newhexsha,actor,time,tz_offset,message):
64+
""":return: New instance of a RefLogEntry"""
65+
ifnotisinstance(actor,Actor):
66+
raiseValueError("Need actor instance, got %s"%actor)
67+
# END check types
68+
returnRefLogEntry((oldhexsha,newhexsha,actor, (time,tz_offset),message))
69+
70+
@classmethod
71+
deffrom_line(self,line):
72+
""":return: New RefLogEntry instance from the given revlog line.
73+
:param line: line without trailing newline
74+
:raise ValueError: If line could not be parsed"""
75+
raiseNotImplementedError("todo")
76+
77+
78+
classRefLog(list,Serializable):
79+
"""A reflog contains reflog entries, each of which defines a certain state
80+
of the head in question. Custom query methods allow to retrieve log entries
81+
by date or by other criteria.
82+
83+
Reflog entries are orded, the first added entry is first in the list, the last
84+
entry, i.e. the last change of the head or reference, is last in the list."""
85+
86+
__slots__=tuple()
87+
88+
#{ Interface
89+
90+
@classmethod
91+
deffrom_file(cls,filepath):
92+
"""
93+
:return: a new RefLog instance containing all entries from the reflog
94+
at the given filepath
95+
:param filepath: path to reflog
96+
:raise ValueError: If the file could not be read or was corrupted in some way"""
97+
inst=cls()
98+
fmap=file_contents_ro_filepath(filepath,stream=False,allow_mmap=True)
99+
try:
100+
inst._deserialize(fmap)
101+
finally:
102+
fmap.close()
103+
#END handle closing of handle
104+
returninst
105+
106+
@classmethod
107+
defreflog_path(cls,ref):
108+
"""
109+
:return: string to absolute path at which the reflog of the given ref
110+
instance would be found. The path is not guaranteed to point to a valid
111+
file though.
112+
:param ref: SymbolicReference instance"""
113+
returnjoin(ref.repo.git_dir,"logs",ref.path)
114+
115+
@classmethod
116+
defiter_entries(cls,stream):
117+
"""
118+
:return: Iterator yielding RefLogEntry instances, one for each line read
119+
sfrom the given stream.
120+
:param stream: file-like object containing the revlog in its native format
121+
or basestring instance pointing to a file to read"""
122+
new_entry=RefLogEntry.from_line
123+
ifisinstance(stream,basestring):
124+
stream=file_contents_ro_filepath(stream)
125+
#END handle stream type
126+
return (new_entry(line.strip())forlineinstream)
127+
128+
defto_file(self,filepath):
129+
"""Write the contents of the reflog instance to a file at the given filepath.
130+
:param filepath: path to file, parent directories are assumed to exist"""
131+
fp=open(filepath,'wb')
132+
try:
133+
self._serialize(fp)
134+
finally:
135+
fp.close()
136+
#END handle file streams
137+
138+
#} END interface
139+
140+
#{ Serializable Interface
141+
def_serialize(self,stream):
142+
lm1=len(self)-1
143+
write=stream.write()
144+
145+
# write all entries
146+
fori,einself:
147+
s=repr(e)
148+
ifi!=lm1:
149+
s+="\n"
150+
#END handle line separator
151+
write(s)
152+
#END for each entry
153+
154+
def_deserialize(self,stream):
155+
new_entry=RefLogEntry.from_line
156+
append=self.append
157+
# NOTE: should use iter_entries, but this way it will be more direct and faster
158+
forlineinstream:
159+
append(new_entry(line.strip()))
160+
#END handle deserializatoin
161+
#} END serializable interface

‎test/test_blob.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
fromgitdb.utilimporthex_to_bin
1010

1111
classTestBlob(TestBase):
12-
13-
deftest_mime_type_should_return_mime_type_for_known_types(self):
14-
blob=Blob(self.rorepo,**{'binsha':Blob.NULL_BIN_SHA,'path':'foo.png'})
15-
assert_equal("image/png",blob.mime_type)
12+
13+
deftest_mime_type_should_return_mime_type_for_known_types(self):
14+
blob=Blob(self.rorepo,**{'binsha':Blob.NULL_BIN_SHA,'path':'foo.png'})
15+
assert_equal("image/png",blob.mime_type)
1616

17-
deftest_mime_type_should_return_text_plain_for_unknown_types(self):
18-
blob=Blob(self.rorepo,**{'binsha':Blob.NULL_BIN_SHA,'path':'something'})
19-
assert_equal("text/plain",blob.mime_type)
17+
deftest_mime_type_should_return_text_plain_for_unknown_types(self):
18+
blob=Blob(self.rorepo,**{'binsha':Blob.NULL_BIN_SHA,'path':'something'})
19+
assert_equal("text/plain",blob.mime_type)
2020

21+
deftest_nodict(self):
22+
self.failUnlessRaises(AttributeError,setattr,self.rorepo.tree()['AUTHORS'],'someattr',2)
23+

‎test/test_commit.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class TestCommit(TestBase):
7070
deftest_bake(self):
7171

7272
commit=self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
73+
# commits have no dict
74+
self.failUnlessRaises(AttributeError,setattr,commit,'someattr',1)
7375
commit.author# bake
7476

7577
assert_equal("Sebastian Thiel",commit.author.name)

‎test/test_reflog.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
fromgit.test.libimport*
2+
fromgit.objectsimportIndexObject,Actor
3+
fromgit.refsimport*
4+
5+
classTestRefLog(TestBase):
6+
7+
deftest_reflogentry(self):
8+
nullhexsha=IndexObject.NULL_HEX_SHA
9+
hexsha='F'*40
10+
actor=Actor('name','email')
11+
msg="message"
12+
13+
self.failUnlessRaises(ValueError,RefLogEntry.new,nullhexsha,hexsha,'noactor',0,0,"")
14+
e=RefLogEntry.new(nullhexsha,hexsha,actor,0,1,msg)
15+
16+
asserte.oldhexsha==nullhexsha
17+
asserte.newhexsha==hexsha
18+
asserte.actor==actor
19+
asserte.time[0]==0
20+
asserte.time[1]==1
21+
asserte.message==msg
22+
23+
# check representation (roughly)
24+
assertrepr(e).startswith(nullhexsha)
25+
26+
deftest_base(self):
27+
pass
28+
# raise on invalid revlog
29+
# TODO: Try multiple corrupted ones !
30+
31+
32+
# test serialize and deserialize - results must match exactly

‎test/test_refs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def test_tag_base(self):
3333
iftag.tagisnotNone:
3434
tag_object_refs.append(tag )
3535
tagobj=tag.tag
36+
# have no dict
37+
self.failUnlessRaises(AttributeError,setattr,tagobj,'someattr',1)
3638
assertisinstance(tagobj,TagObject )
3739
asserttagobj.tag==tag.name
3840
assertisinstance(tagobj.tagger,Actor )

‎test/test_tree.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ def test_serializable(self):
2323
continue
2424
# END skip non-trees
2525
tree=item
26+
# trees have no dict
27+
self.failUnlessRaises(AttributeError,setattr,tree,'someattr',1)
28+
2629
orig_data=tree.data_stream.read()
2730
orig_cache=tree._cache
2831

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp