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