1 # A minimal client for Mercurial's command server |
1 # A minimal client for Mercurial's command server |
2 |
2 |
3 from __future__ import absolute_import, print_function |
3 from __future__ import absolute_import, print_function |
|
4 |
|
5 import io |
4 import os |
6 import os |
|
7 import re |
5 import signal |
8 import signal |
6 import socket |
9 import socket |
7 import struct |
10 import struct |
8 import subprocess |
11 import subprocess |
9 import sys |
12 import sys |
10 import time |
13 import time |
11 |
14 |
12 try: |
15 if sys.version_info[0] >= 3: |
13 import cStringIO as io |
16 stdout = sys.stdout.buffer |
14 stringio = io.StringIO |
17 stderr = sys.stderr.buffer |
15 except ImportError: |
18 stringio = io.BytesIO |
16 import io |
19 def bprint(*args): |
17 stringio = io.StringIO |
20 # remove b'' as well for ease of test migration |
|
21 pargs = [re.sub(br'''\bb(['"])''', br'\1', b'%s' % a) for a in args] |
|
22 stdout.write(b' '.join(pargs) + b'\n') |
|
23 else: |
|
24 import cStringIO |
|
25 stdout = sys.stdout |
|
26 stderr = sys.stderr |
|
27 stringio = cStringIO.StringIO |
|
28 bprint = print |
18 |
29 |
19 def connectpipe(path=None): |
30 def connectpipe(path=None): |
20 cmdline = ['hg', 'serve', '--cmdserver', 'pipe'] |
31 cmdline = [b'hg', b'serve', b'--cmdserver', b'pipe'] |
21 if path: |
32 if path: |
22 cmdline += ['-R', path] |
33 cmdline += [b'-R', path] |
23 |
34 |
24 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE, |
35 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE, |
25 stdout=subprocess.PIPE) |
36 stdout=subprocess.PIPE) |
26 |
37 |
27 return server |
38 return server |
39 self.sock.close() |
50 self.sock.close() |
40 |
51 |
41 class unixserver(object): |
52 class unixserver(object): |
42 def __init__(self, sockpath, logpath=None, repopath=None): |
53 def __init__(self, sockpath, logpath=None, repopath=None): |
43 self.sockpath = sockpath |
54 self.sockpath = sockpath |
44 cmdline = ['hg', 'serve', '--cmdserver', 'unix', '-a', sockpath] |
55 cmdline = [b'hg', b'serve', b'--cmdserver', b'unix', b'-a', sockpath] |
45 if repopath: |
56 if repopath: |
46 cmdline += ['-R', repopath] |
57 cmdline += [b'-R', repopath] |
47 if logpath: |
58 if logpath: |
48 stdout = open(logpath, 'a') |
59 stdout = open(logpath, 'a') |
49 stderr = subprocess.STDOUT |
60 stderr = subprocess.STDOUT |
50 else: |
61 else: |
51 stdout = stderr = None |
62 stdout = stderr = None |
62 def shutdown(self): |
73 def shutdown(self): |
63 os.kill(self.server.pid, signal.SIGTERM) |
74 os.kill(self.server.pid, signal.SIGTERM) |
64 self.server.wait() |
75 self.server.wait() |
65 |
76 |
66 def writeblock(server, data): |
77 def writeblock(server, data): |
67 server.stdin.write(struct.pack('>I', len(data))) |
78 server.stdin.write(struct.pack(b'>I', len(data))) |
68 server.stdin.write(data) |
79 server.stdin.write(data) |
69 server.stdin.flush() |
80 server.stdin.flush() |
70 |
81 |
71 def readchannel(server): |
82 def readchannel(server): |
72 data = server.stdout.read(5) |
83 data = server.stdout.read(5) |
73 if not data: |
84 if not data: |
74 raise EOFError |
85 raise EOFError |
75 channel, length = struct.unpack('>cI', data) |
86 channel, length = struct.unpack('>cI', data) |
76 if channel in 'IL': |
87 if channel in b'IL': |
77 return channel, length |
88 return channel, length |
78 else: |
89 else: |
79 return channel, server.stdout.read(length) |
90 return channel, server.stdout.read(length) |
80 |
91 |
81 def sep(text): |
92 def sep(text): |
82 return text.replace('\\', '/') |
93 return text.replace(b'\\', b'/') |
83 |
94 |
84 def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None, |
95 def runcommand(server, args, output=stdout, error=stderr, input=None, |
85 outfilter=lambda x: x): |
96 outfilter=lambda x: x): |
86 print('*** runcommand', ' '.join(args)) |
97 bprint(b'*** runcommand', b' '.join(args)) |
87 sys.stdout.flush() |
98 stdout.flush() |
88 server.stdin.write('runcommand\n') |
99 server.stdin.write(b'runcommand\n') |
89 writeblock(server, '\0'.join(args)) |
100 writeblock(server, b'\0'.join(args)) |
90 |
101 |
91 if not input: |
102 if not input: |
92 input = stringio() |
103 input = stringio() |
93 |
104 |
94 while True: |
105 while True: |
95 ch, data = readchannel(server) |
106 ch, data = readchannel(server) |
96 if ch == 'o': |
107 if ch == b'o': |
97 output.write(outfilter(data)) |
108 output.write(outfilter(data)) |
98 output.flush() |
109 output.flush() |
99 elif ch == 'e': |
110 elif ch == b'e': |
100 error.write(data) |
111 error.write(data) |
101 error.flush() |
112 error.flush() |
102 elif ch == 'I': |
113 elif ch == b'I': |
103 writeblock(server, input.read(data)) |
114 writeblock(server, input.read(data)) |
104 elif ch == 'L': |
115 elif ch == b'L': |
105 writeblock(server, input.readline(data)) |
116 writeblock(server, input.readline(data)) |
106 elif ch == 'r': |
117 elif ch == b'r': |
107 ret, = struct.unpack('>i', data) |
118 ret, = struct.unpack('>i', data) |
108 if ret != 0: |
119 if ret != 0: |
109 print(' [%d]' % ret) |
120 bprint(b' [%d]' % ret) |
110 return ret |
121 return ret |
111 else: |
122 else: |
112 print("unexpected channel %c: %r" % (ch, data)) |
123 bprint(b"unexpected channel %c: %r" % (ch, data)) |
113 if ch.isupper(): |
124 if ch.isupper(): |
114 return |
125 return |
115 |
126 |
116 def check(func, connect=connectpipe): |
127 def check(func, connect=connectpipe): |
117 sys.stdout.flush() |
128 stdout.flush() |
118 server = connect() |
129 server = connect() |
119 try: |
130 try: |
120 return func(server) |
131 return func(server) |
121 finally: |
132 finally: |
122 server.stdin.close() |
133 server.stdin.close() |