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