Show More
@@ -1,142 +1,147 | |||||
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 |
|
4 | |||
5 | import io |
|
5 | import io | |
6 | import os |
|
6 | import os | |
7 | import re |
|
7 | import re | |
8 | import signal |
|
8 | import signal | |
9 | import socket |
|
9 | import socket | |
10 | import struct |
|
10 | import struct | |
11 | import subprocess |
|
11 | import subprocess | |
12 | import sys |
|
12 | import sys | |
13 | import time |
|
13 | import time | |
14 |
|
14 | |||
15 | if sys.version_info[0] >= 3: |
|
15 | if sys.version_info[0] >= 3: | |
16 | stdout = sys.stdout.buffer |
|
16 | stdout = sys.stdout.buffer | |
17 | stderr = sys.stderr.buffer |
|
17 | stderr = sys.stderr.buffer | |
18 | stringio = io.BytesIO |
|
18 | stringio = io.BytesIO | |
19 | def bprint(*args): |
|
19 | def bprint(*args): | |
20 | # remove b'' as well for ease of test migration |
|
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] |
|
21 | pargs = [re.sub(br'''\bb(['"])''', br'\1', b'%s' % a) for a in args] | |
22 | stdout.write(b' '.join(pargs) + b'\n') |
|
22 | stdout.write(b' '.join(pargs) + b'\n') | |
23 | else: |
|
23 | else: | |
24 | import cStringIO |
|
24 | import cStringIO | |
25 | stdout = sys.stdout |
|
25 | stdout = sys.stdout | |
26 | stderr = sys.stderr |
|
26 | stderr = sys.stderr | |
27 | stringio = cStringIO.StringIO |
|
27 | stringio = cStringIO.StringIO | |
28 | bprint = print |
|
28 | bprint = print | |
29 |
|
29 | |||
30 | def connectpipe(path=None, extraargs=()): |
|
30 | def connectpipe(path=None, extraargs=()): | |
31 | cmdline = [b'hg', b'serve', b'--cmdserver', b'pipe'] |
|
31 | cmdline = [b'hg', b'serve', b'--cmdserver', b'pipe'] | |
32 | if path: |
|
32 | if path: | |
33 | cmdline += [b'-R', path] |
|
33 | cmdline += [b'-R', path] | |
34 | cmdline.extend(extraargs) |
|
34 | cmdline.extend(extraargs) | |
35 |
|
35 | |||
36 | server = subprocess.Popen(cmdline, stdin=subprocess.PIPE, |
|
36 | def tonative(cmdline): | |
|
37 | if os.name != r'nt': | |||
|
38 | return cmdline | |||
|
39 | return [arg.decode("utf-8") for arg in cmdline] | |||
|
40 | ||||
|
41 | server = subprocess.Popen(tonative(cmdline), stdin=subprocess.PIPE, | |||
37 | stdout=subprocess.PIPE) |
|
42 | stdout=subprocess.PIPE) | |
38 |
|
43 | |||
39 | return server |
|
44 | return server | |
40 |
|
45 | |||
41 | class unixconnection(object): |
|
46 | class unixconnection(object): | |
42 | def __init__(self, sockpath): |
|
47 | def __init__(self, sockpath): | |
43 | self.sock = sock = socket.socket(socket.AF_UNIX) |
|
48 | self.sock = sock = socket.socket(socket.AF_UNIX) | |
44 | sock.connect(sockpath) |
|
49 | sock.connect(sockpath) | |
45 | self.stdin = sock.makefile('wb') |
|
50 | self.stdin = sock.makefile('wb') | |
46 | self.stdout = sock.makefile('rb') |
|
51 | self.stdout = sock.makefile('rb') | |
47 |
|
52 | |||
48 | def wait(self): |
|
53 | def wait(self): | |
49 | self.stdin.close() |
|
54 | self.stdin.close() | |
50 | self.stdout.close() |
|
55 | self.stdout.close() | |
51 | self.sock.close() |
|
56 | self.sock.close() | |
52 |
|
57 | |||
53 | class unixserver(object): |
|
58 | class unixserver(object): | |
54 | def __init__(self, sockpath, logpath=None, repopath=None): |
|
59 | def __init__(self, sockpath, logpath=None, repopath=None): | |
55 | self.sockpath = sockpath |
|
60 | self.sockpath = sockpath | |
56 | cmdline = [b'hg', b'serve', b'--cmdserver', b'unix', b'-a', sockpath] |
|
61 | cmdline = [b'hg', b'serve', b'--cmdserver', b'unix', b'-a', sockpath] | |
57 | if repopath: |
|
62 | if repopath: | |
58 | cmdline += [b'-R', repopath] |
|
63 | cmdline += [b'-R', repopath] | |
59 | if logpath: |
|
64 | if logpath: | |
60 | stdout = open(logpath, 'a') |
|
65 | stdout = open(logpath, 'a') | |
61 | stderr = subprocess.STDOUT |
|
66 | stderr = subprocess.STDOUT | |
62 | else: |
|
67 | else: | |
63 | stdout = stderr = None |
|
68 | stdout = stderr = None | |
64 | self.server = subprocess.Popen(cmdline, stdout=stdout, stderr=stderr) |
|
69 | self.server = subprocess.Popen(cmdline, stdout=stdout, stderr=stderr) | |
65 | # wait for listen() |
|
70 | # wait for listen() | |
66 | while self.server.poll() is None: |
|
71 | while self.server.poll() is None: | |
67 | if os.path.exists(sockpath): |
|
72 | if os.path.exists(sockpath): | |
68 | break |
|
73 | break | |
69 | time.sleep(0.1) |
|
74 | time.sleep(0.1) | |
70 |
|
75 | |||
71 | def connect(self): |
|
76 | def connect(self): | |
72 | return unixconnection(self.sockpath) |
|
77 | return unixconnection(self.sockpath) | |
73 |
|
78 | |||
74 | def shutdown(self): |
|
79 | def shutdown(self): | |
75 | os.kill(self.server.pid, signal.SIGTERM) |
|
80 | os.kill(self.server.pid, signal.SIGTERM) | |
76 | self.server.wait() |
|
81 | self.server.wait() | |
77 |
|
82 | |||
78 | def writeblock(server, data): |
|
83 | def writeblock(server, data): | |
79 | server.stdin.write(struct.pack(b'>I', len(data))) |
|
84 | server.stdin.write(struct.pack(b'>I', len(data))) | |
80 | server.stdin.write(data) |
|
85 | server.stdin.write(data) | |
81 | server.stdin.flush() |
|
86 | server.stdin.flush() | |
82 |
|
87 | |||
83 | def readchannel(server): |
|
88 | def readchannel(server): | |
84 | data = server.stdout.read(5) |
|
89 | data = server.stdout.read(5) | |
85 | if not data: |
|
90 | if not data: | |
86 | raise EOFError |
|
91 | raise EOFError | |
87 | channel, length = struct.unpack('>cI', data) |
|
92 | channel, length = struct.unpack('>cI', data) | |
88 | if channel in b'IL': |
|
93 | if channel in b'IL': | |
89 | return channel, length |
|
94 | return channel, length | |
90 | else: |
|
95 | else: | |
91 | return channel, server.stdout.read(length) |
|
96 | return channel, server.stdout.read(length) | |
92 |
|
97 | |||
93 | def sep(text): |
|
98 | def sep(text): | |
94 | return text.replace(b'\\', b'/') |
|
99 | return text.replace(b'\\', b'/') | |
95 |
|
100 | |||
96 | def runcommand(server, args, output=stdout, error=stderr, input=None, |
|
101 | def runcommand(server, args, output=stdout, error=stderr, input=None, | |
97 | outfilter=lambda x: x): |
|
102 | outfilter=lambda x: x): | |
98 | bprint(b'*** runcommand', b' '.join(args)) |
|
103 | bprint(b'*** runcommand', b' '.join(args)) | |
99 | stdout.flush() |
|
104 | stdout.flush() | |
100 | server.stdin.write(b'runcommand\n') |
|
105 | server.stdin.write(b'runcommand\n') | |
101 | writeblock(server, b'\0'.join(args)) |
|
106 | writeblock(server, b'\0'.join(args)) | |
102 |
|
107 | |||
103 | if not input: |
|
108 | if not input: | |
104 | input = stringio() |
|
109 | input = stringio() | |
105 |
|
110 | |||
106 | while True: |
|
111 | while True: | |
107 | ch, data = readchannel(server) |
|
112 | ch, data = readchannel(server) | |
108 | if ch == b'o': |
|
113 | if ch == b'o': | |
109 | output.write(outfilter(data)) |
|
114 | output.write(outfilter(data)) | |
110 | output.flush() |
|
115 | output.flush() | |
111 | elif ch == b'e': |
|
116 | elif ch == b'e': | |
112 | error.write(data) |
|
117 | error.write(data) | |
113 | error.flush() |
|
118 | error.flush() | |
114 | elif ch == b'I': |
|
119 | elif ch == b'I': | |
115 | writeblock(server, input.read(data)) |
|
120 | writeblock(server, input.read(data)) | |
116 | elif ch == b'L': |
|
121 | elif ch == b'L': | |
117 | writeblock(server, input.readline(data)) |
|
122 | writeblock(server, input.readline(data)) | |
118 | elif ch == b'm': |
|
123 | elif ch == b'm': | |
119 | bprint(b"message: %r" % data) |
|
124 | bprint(b"message: %r" % data) | |
120 | elif ch == b'r': |
|
125 | elif ch == b'r': | |
121 | ret, = struct.unpack('>i', data) |
|
126 | ret, = struct.unpack('>i', data) | |
122 | if ret != 0: |
|
127 | if ret != 0: | |
123 | bprint(b' [%d]' % ret) |
|
128 | bprint(b' [%d]' % ret) | |
124 | return ret |
|
129 | return ret | |
125 | else: |
|
130 | else: | |
126 | bprint(b"unexpected channel %c: %r" % (ch, data)) |
|
131 | bprint(b"unexpected channel %c: %r" % (ch, data)) | |
127 | if ch.isupper(): |
|
132 | if ch.isupper(): | |
128 | return |
|
133 | return | |
129 |
|
134 | |||
130 | def check(func, connect=connectpipe): |
|
135 | def check(func, connect=connectpipe): | |
131 | stdout.flush() |
|
136 | stdout.flush() | |
132 | server = connect() |
|
137 | server = connect() | |
133 | try: |
|
138 | try: | |
134 | return func(server) |
|
139 | return func(server) | |
135 | finally: |
|
140 | finally: | |
136 | server.stdin.close() |
|
141 | server.stdin.close() | |
137 | server.wait() |
|
142 | server.wait() | |
138 |
|
143 | |||
139 | def checkwith(connect=connectpipe, **kwargs): |
|
144 | def checkwith(connect=connectpipe, **kwargs): | |
140 | def wrap(func): |
|
145 | def wrap(func): | |
141 | return check(func, lambda: connect(**kwargs)) |
|
146 | return check(func, lambda: connect(**kwargs)) | |
142 | return wrap |
|
147 | return wrap |
General Comments 0
You need to be logged in to leave comments.
Login now