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