##// END OF EJS Templates
cmdserver: drop useless in_ attribute from channeledoutput...
Yuya Nishihara -
r22563:8cc5e673 default
parent child Browse files
Show More
@@ -1,251 +1,250 b''
1 1 # commandserver.py - communicate with Mercurial's API over a pipe
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import struct
10 10 import sys, os
11 11 import dispatch, encoding, util
12 12
13 13 logfile = None
14 14
15 15 def log(*args):
16 16 if not logfile:
17 17 return
18 18
19 19 for a in args:
20 20 logfile.write(str(a))
21 21
22 22 logfile.flush()
23 23
24 24 class channeledoutput(object):
25 25 """
26 26 Write data to out in the following format:
27 27
28 28 data length (unsigned int),
29 29 data
30 30 """
31 def __init__(self, in_, out, channel):
32 self.in_ = in_
31 def __init__(self, out, channel):
33 32 self.out = out
34 33 self.channel = channel
35 34
36 35 def write(self, data):
37 36 if not data:
38 37 return
39 38 self.out.write(struct.pack('>cI', self.channel, len(data)))
40 39 self.out.write(data)
41 40 self.out.flush()
42 41
43 42 def __getattr__(self, attr):
44 43 if attr in ('isatty', 'fileno'):
45 44 raise AttributeError(attr)
46 return getattr(self.in_, attr)
45 return getattr(self.out, attr)
47 46
48 47 class channeledinput(object):
49 48 """
50 49 Read data from in_.
51 50
52 51 Requests for input are written to out in the following format:
53 52 channel identifier - 'I' for plain input, 'L' line based (1 byte)
54 53 how many bytes to send at most (unsigned int),
55 54
56 55 The client replies with:
57 56 data length (unsigned int), 0 meaning EOF
58 57 data
59 58 """
60 59
61 60 maxchunksize = 4 * 1024
62 61
63 62 def __init__(self, in_, out, channel):
64 63 self.in_ = in_
65 64 self.out = out
66 65 self.channel = channel
67 66
68 67 def read(self, size=-1):
69 68 if size < 0:
70 69 # if we need to consume all the clients input, ask for 4k chunks
71 70 # so the pipe doesn't fill up risking a deadlock
72 71 size = self.maxchunksize
73 72 s = self._read(size, self.channel)
74 73 buf = s
75 74 while s:
76 75 s = self._read(size, self.channel)
77 76 buf += s
78 77
79 78 return buf
80 79 else:
81 80 return self._read(size, self.channel)
82 81
83 82 def _read(self, size, channel):
84 83 if not size:
85 84 return ''
86 85 assert size > 0
87 86
88 87 # tell the client we need at most size bytes
89 88 self.out.write(struct.pack('>cI', channel, size))
90 89 self.out.flush()
91 90
92 91 length = self.in_.read(4)
93 92 length = struct.unpack('>I', length)[0]
94 93 if not length:
95 94 return ''
96 95 else:
97 96 return self.in_.read(length)
98 97
99 98 def readline(self, size=-1):
100 99 if size < 0:
101 100 size = self.maxchunksize
102 101 s = self._read(size, 'L')
103 102 buf = s
104 103 # keep asking for more until there's either no more or
105 104 # we got a full line
106 105 while s and s[-1] != '\n':
107 106 s = self._read(size, 'L')
108 107 buf += s
109 108
110 109 return buf
111 110 else:
112 111 return self._read(size, 'L')
113 112
114 113 def __iter__(self):
115 114 return self
116 115
117 116 def next(self):
118 117 l = self.readline()
119 118 if not l:
120 119 raise StopIteration
121 120 return l
122 121
123 122 def __getattr__(self, attr):
124 123 if attr in ('isatty', 'fileno'):
125 124 raise AttributeError(attr)
126 125 return getattr(self.in_, attr)
127 126
128 127 class server(object):
129 128 """
130 129 Listens for commands on stdin, runs them and writes the output on a channel
131 130 based stream to stdout.
132 131 """
133 132 def __init__(self, ui, repo, mode):
134 133 self.cwd = os.getcwd()
135 134
136 135 logpath = ui.config("cmdserver", "log", None)
137 136 if logpath:
138 137 global logfile
139 138 if logpath == '-':
140 139 # write log on a special 'd' (debug) channel
141 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
140 logfile = channeledoutput(sys.stdout, 'd')
142 141 else:
143 142 logfile = open(logpath, 'a')
144 143
145 144 if repo:
146 145 # the ui here is really the repo ui so take its baseui so we don't
147 146 # end up with its local configuration
148 147 self.ui = repo.baseui
149 148 self.repo = repo
150 149 self.repoui = repo.ui
151 150 else:
152 151 self.ui = ui
153 152 self.repo = self.repoui = None
154 153
155 154 if mode == 'pipe':
156 self.cerr = channeledoutput(sys.stdout, sys.stdout, 'e')
157 self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
155 self.cerr = channeledoutput(sys.stdout, 'e')
156 self.cout = channeledoutput(sys.stdout, 'o')
158 157 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
159 self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
158 self.cresult = channeledoutput(sys.stdout, 'r')
160 159
161 160 self.client = sys.stdin
162 161 else:
163 162 raise util.Abort(_('unknown mode %s') % mode)
164 163
165 164 def _read(self, size):
166 165 if not size:
167 166 return ''
168 167
169 168 data = self.client.read(size)
170 169
171 170 # is the other end closed?
172 171 if not data:
173 172 raise EOFError
174 173
175 174 return data
176 175
177 176 def runcommand(self):
178 177 """ reads a list of \0 terminated arguments, executes
179 178 and writes the return code to the result channel """
180 179
181 180 length = struct.unpack('>I', self._read(4))[0]
182 181 if not length:
183 182 args = []
184 183 else:
185 184 args = self._read(length).split('\0')
186 185
187 186 # copy the uis so changes (e.g. --config or --verbose) don't
188 187 # persist between requests
189 188 copiedui = self.ui.copy()
190 189 uis = [copiedui]
191 190 if self.repo:
192 191 self.repo.baseui = copiedui
193 192 # clone ui without using ui.copy because this is protected
194 193 repoui = self.repoui.__class__(self.repoui)
195 194 repoui.copy = copiedui.copy # redo copy protection
196 195 uis.append(repoui)
197 196 self.repo.ui = self.repo.dirstate._ui = repoui
198 197 self.repo.invalidateall()
199 198
200 199 for ui in uis:
201 200 # any kind of interaction must use server channels
202 201 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
203 202
204 203 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
205 204 self.cout, self.cerr)
206 205
207 206 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
208 207
209 208 # restore old cwd
210 209 if '--cwd' in args:
211 210 os.chdir(self.cwd)
212 211
213 212 self.cresult.write(struct.pack('>i', int(ret)))
214 213
215 214 def getencoding(self):
216 215 """ writes the current encoding to the result channel """
217 216 self.cresult.write(encoding.encoding)
218 217
219 218 def serveone(self):
220 219 cmd = self.client.readline()[:-1]
221 220 if cmd:
222 221 handler = self.capabilities.get(cmd)
223 222 if handler:
224 223 handler(self)
225 224 else:
226 225 # clients are expected to check what commands are supported by
227 226 # looking at the servers capabilities
228 227 raise util.Abort(_('unknown command %s') % cmd)
229 228
230 229 return cmd != ''
231 230
232 231 capabilities = {'runcommand' : runcommand,
233 232 'getencoding' : getencoding}
234 233
235 234 def serve(self):
236 235 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
237 236 hellomsg += '\n'
238 237 hellomsg += 'encoding: ' + encoding.encoding
239 238
240 239 # write the hello msg in -one- chunk
241 240 self.cout.write(hellomsg)
242 241
243 242 try:
244 243 while self.serveone():
245 244 pass
246 245 except EOFError:
247 246 # we'll get here if the client disconnected while we were reading
248 247 # its request
249 248 return 1
250 249
251 250 return 0
General Comments 0
You need to be logged in to leave comments. Login now