comparison hgext/fastannotate/protocol.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 876494fd967d
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
20 ) 20 )
21 from . import context 21 from . import context
22 22
23 # common 23 # common
24 24
25
25 def _getmaster(ui): 26 def _getmaster(ui):
26 """get the mainbranch, and enforce it is set""" 27 """get the mainbranch, and enforce it is set"""
27 master = ui.config('fastannotate', 'mainbranch') 28 master = ui.config('fastannotate', 'mainbranch')
28 if not master: 29 if not master:
29 raise error.Abort(_('fastannotate.mainbranch is required ' 30 raise error.Abort(
30 'for both the client and the server')) 31 _(
32 'fastannotate.mainbranch is required '
33 'for both the client and the server'
34 )
35 )
31 return master 36 return master
32 37
38
33 # server-side 39 # server-side
40
34 41
35 def _capabilities(orig, repo, proto): 42 def _capabilities(orig, repo, proto):
36 result = orig(repo, proto) 43 result = orig(repo, proto)
37 result.append('getannotate') 44 result.append('getannotate')
38 return result 45 return result
46
39 47
40 def _getannotate(repo, proto, path, lastnode): 48 def _getannotate(repo, proto, path, lastnode):
41 # output: 49 # output:
42 # FILE := vfspath + '\0' + str(size) + '\0' + content 50 # FILE := vfspath + '\0' + str(size) + '\0' + content
43 # OUTPUT := '' | FILE + OUTPUT 51 # OUTPUT := '' | FILE + OUTPUT
44 result = '' 52 result = ''
45 buildondemand = repo.ui.configbool('fastannotate', 'serverbuildondemand', 53 buildondemand = repo.ui.configbool(
46 True) 54 'fastannotate', 'serverbuildondemand', True
55 )
47 with context.annotatecontext(repo, path) as actx: 56 with context.annotatecontext(repo, path) as actx:
48 if buildondemand: 57 if buildondemand:
49 # update before responding to the client 58 # update before responding to the client
50 master = _getmaster(repo.ui) 59 master = _getmaster(repo.ui)
51 try: 60 try:
55 # non-fast-forward move or corrupted. rebuild automically. 64 # non-fast-forward move or corrupted. rebuild automically.
56 actx.rebuild() 65 actx.rebuild()
57 try: 66 try:
58 actx.annotate(master, master) 67 actx.annotate(master, master)
59 except Exception: 68 except Exception:
60 actx.rebuild() # delete files 69 actx.rebuild() # delete files
61 finally: 70 finally:
62 # although the "with" context will also do a close/flush, we 71 # although the "with" context will also do a close/flush, we
63 # need to do it early so we can send the correct respond to 72 # need to do it early so we can send the correct respond to
64 # client. 73 # client.
65 actx.close() 74 actx.close()
76 vfsbaselen = len(repo.vfs.base + '/') 85 vfsbaselen = len(repo.vfs.base + '/')
77 relpath = p[vfsbaselen:] 86 relpath = p[vfsbaselen:]
78 result += '%s\0%d\0%s' % (relpath, len(content), content) 87 result += '%s\0%d\0%s' % (relpath, len(content), content)
79 return result 88 return result
80 89
90
81 def _registerwireprotocommand(): 91 def _registerwireprotocommand():
82 if 'getannotate' in wireprotov1server.commands: 92 if 'getannotate' in wireprotov1server.commands:
83 return 93 return
84 wireprotov1server.wireprotocommand( 94 wireprotov1server.wireprotocommand('getannotate', 'path lastnode')(
85 'getannotate', 'path lastnode')(_getannotate) 95 _getannotate
96 )
97
86 98
87 def serveruisetup(ui): 99 def serveruisetup(ui):
88 _registerwireprotocommand() 100 _registerwireprotocommand()
89 extensions.wrapfunction(wireprotov1server, '_capabilities', _capabilities) 101 extensions.wrapfunction(wireprotov1server, '_capabilities', _capabilities)
90 102
103
91 # client-side 104 # client-side
105
92 106
93 def _parseresponse(payload): 107 def _parseresponse(payload):
94 result = {} 108 result = {}
95 i = 0 109 i = 0
96 l = len(payload) - 1 110 l = len(payload) - 1
97 state = 0 # 0: vfspath, 1: size 111 state = 0 # 0: vfspath, 1: size
98 vfspath = size = '' 112 vfspath = size = ''
99 while i < l: 113 while i < l:
100 ch = payload[i:i + 1] 114 ch = payload[i : i + 1]
101 if ch == '\0': 115 if ch == '\0':
102 if state == 1: 116 if state == 1:
103 result[vfspath] = payload[i + 1:i + 1 + int(size)] 117 result[vfspath] = payload[i + 1 : i + 1 + int(size)]
104 i += int(size) 118 i += int(size)
105 state = 0 119 state = 0
106 vfspath = size = '' 120 vfspath = size = ''
107 elif state == 0: 121 elif state == 0:
108 state = 1 122 state = 1
111 size += ch 125 size += ch
112 elif state == 0: 126 elif state == 0:
113 vfspath += ch 127 vfspath += ch
114 i += 1 128 i += 1
115 return result 129 return result
130
116 131
117 def peersetup(ui, peer): 132 def peersetup(ui, peer):
118 class fastannotatepeer(peer.__class__): 133 class fastannotatepeer(peer.__class__):
119 @wireprotov1peer.batchable 134 @wireprotov1peer.batchable
120 def getannotate(self, path, lastnode=None): 135 def getannotate(self, path, lastnode=None):
124 else: 139 else:
125 args = {'path': path, 'lastnode': lastnode or ''} 140 args = {'path': path, 'lastnode': lastnode or ''}
126 f = wireprotov1peer.future() 141 f = wireprotov1peer.future()
127 yield args, f 142 yield args, f
128 yield _parseresponse(f.value) 143 yield _parseresponse(f.value)
144
129 peer.__class__ = fastannotatepeer 145 peer.__class__ = fastannotatepeer
146
130 147
131 @contextlib.contextmanager 148 @contextlib.contextmanager
132 def annotatepeer(repo): 149 def annotatepeer(repo):
133 ui = repo.ui 150 ui = repo.ui
134 151
135 remotepath = ui.expandpath( 152 remotepath = ui.expandpath(
136 ui.config('fastannotate', 'remotepath', 'default')) 153 ui.config('fastannotate', 'remotepath', 'default')
154 )
137 peer = hg.peer(ui, {}, remotepath) 155 peer = hg.peer(ui, {}, remotepath)
138 156
139 try: 157 try:
140 yield peer 158 yield peer
141 finally: 159 finally:
142 peer.close() 160 peer.close()
161
143 162
144 def clientfetch(repo, paths, lastnodemap=None, peer=None): 163 def clientfetch(repo, paths, lastnodemap=None, peer=None):
145 """download annotate cache from the server for paths""" 164 """download annotate cache from the server for paths"""
146 if not paths: 165 if not paths:
147 return 166 return
156 ui = repo.ui 175 ui = repo.ui
157 results = [] 176 results = []
158 with peer.commandexecutor() as batcher: 177 with peer.commandexecutor() as batcher:
159 ui.debug('fastannotate: requesting %d files\n' % len(paths)) 178 ui.debug('fastannotate: requesting %d files\n' % len(paths))
160 for p in paths: 179 for p in paths:
161 results.append(batcher.callcommand( 180 results.append(
162 'getannotate', 181 batcher.callcommand(
163 {'path': p, 'lastnode':lastnodemap.get(p)})) 182 'getannotate', {'path': p, 'lastnode': lastnodemap.get(p)}
183 )
184 )
164 185
165 for result in results: 186 for result in results:
166 r = result.result() 187 r = result.result()
167 # TODO: pconvert these paths on the server? 188 # TODO: pconvert these paths on the server?
168 r = {util.pconvert(p): v for p, v in r.iteritems()} 189 r = {util.pconvert(p): v for p, v in r.iteritems()}
169 for path in sorted(r): 190 for path in sorted(r):
170 # ignore malicious paths 191 # ignore malicious paths
171 if (not path.startswith('fastannotate/') 192 if not path.startswith('fastannotate/') or '/../' in (
172 or '/../' in (path + '/')): 193 path + '/'
194 ):
173 ui.debug('fastannotate: ignored malicious path %s\n' % path) 195 ui.debug('fastannotate: ignored malicious path %s\n' % path)
174 continue 196 continue
175 content = r[path] 197 content = r[path]
176 if ui.debugflag: 198 if ui.debugflag:
177 ui.debug('fastannotate: writing %d bytes to %s\n' 199 ui.debug(
178 % (len(content), path)) 200 'fastannotate: writing %d bytes to %s\n'
201 % (len(content), path)
202 )
179 repo.vfs.makedirs(os.path.dirname(path)) 203 repo.vfs.makedirs(os.path.dirname(path))
180 with repo.vfs(path, 'wb') as f: 204 with repo.vfs(path, 'wb') as f:
181 f.write(content) 205 f.write(content)
206
182 207
183 def _filterfetchpaths(repo, paths): 208 def _filterfetchpaths(repo, paths):
184 """return a subset of paths whose history is long and need to fetch linelog 209 """return a subset of paths whose history is long and need to fetch linelog
185 from the server. works with remotefilelog and non-remotefilelog repos. 210 from the server. works with remotefilelog and non-remotefilelog repos.
186 """ 211 """
191 result = [] 216 result = []
192 for path in paths: 217 for path in paths:
193 try: 218 try:
194 if len(repo.file(path)) >= threshold: 219 if len(repo.file(path)) >= threshold:
195 result.append(path) 220 result.append(path)
196 except Exception: # file not found etc. 221 except Exception: # file not found etc.
197 result.append(path) 222 result.append(path)
198 223
199 return result 224 return result
225
200 226
201 def localreposetup(ui, repo): 227 def localreposetup(ui, repo):
202 class fastannotaterepo(repo.__class__): 228 class fastannotaterepo(repo.__class__):
203 def prefetchfastannotate(self, paths, peer=None): 229 def prefetchfastannotate(self, paths, peer=None):
204 master = _getmaster(self.ui) 230 master = _getmaster(self.ui)
213 if needupdatepaths: 239 if needupdatepaths:
214 clientfetch(self, needupdatepaths, lastnodemap, peer) 240 clientfetch(self, needupdatepaths, lastnodemap, peer)
215 except Exception as ex: 241 except Exception as ex:
216 # could be directory not writable or so, not fatal 242 # could be directory not writable or so, not fatal
217 self.ui.debug('fastannotate: prefetch failed: %r\n' % ex) 243 self.ui.debug('fastannotate: prefetch failed: %r\n' % ex)
244
218 repo.__class__ = fastannotaterepo 245 repo.__class__ = fastannotaterepo
246
219 247
220 def clientreposetup(ui, repo): 248 def clientreposetup(ui, repo):
221 _registerwireprotocommand() 249 _registerwireprotocommand()
222 if repo.local(): 250 if repo.local():
223 localreposetup(ui, repo) 251 localreposetup(ui, repo)