/
rpcserver.py
152 lines (124 loc) · 3.62 KB
/
rpcserver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# coding: utf-8
#
# rpc python server
# example rpc client
#
# import xmlrpc.client
# s = xmlrpc.client.ServerProxy("http://localhost:17320")
# s.run_python_code("a = 1")
# print(s.run_python_code('print(a)))
#
## output: 1
import argparse
import io
import os
import signal
import sys
import time
import traceback
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
import uiautomator2
_globals = {
"sys": "sys",
"time": time,
"uiautomator2": uiautomator2,
"u2": uiautomator2
}
class StringBuffer():
def __init__(self):
self.encoding = 'utf-8'
self.buf = io.BytesIO()
def write(self, data):
if isinstance(data, str):
data = data.encode(self.encoding)
self.buf.write(data)
def getvalue(self):
return self.buf.getvalue().decode(self.encoding)
class FakeOutput(object):
def __init__(self):
self._fakeout = StringBuffer() #io.StringIO()
@property
def output(self):
return self._fakeout.getvalue()
def __enter__(self):
self.sys_stdout = sys.stdout
self.sys_stderr = sys.stderr
sys.stdout = self._fakeout
sys.stderr = self._fakeout
return self
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout = self.sys_stdout
sys.stderr = self.sys_stderr
__cached_devices = {}
def _connect(device_id: str):
"""
Returns:
(u2.Device, fixed-device-id)
"""
platform, uri = device_id.split(":", maxsplit=1)
if platform == "android":
d = uiautomator2.connect(uri)
_id = "android:" + d.serial
return d, _id
elif platform == "ios":
import wda
d = wda.Client(uri)
return d.session(), device_id
else:
raise RuntimeError("unknown platform", platform)
def get_device(device_id: str):
"""
Returns:
u2.Device
"""
d = __cached_devices.get(device_id)
if d:
return d
d, _id = _connect(device_id)
if device_id.startswith("android:"):
__cached_devices[_id] = d
return d
def run_python_code(device_id: str, code: str):
is_eval = True
compiled_code = None
try:
compiled_code = compile(code, "<string>", "eval")
except SyntaxError:
is_eval = False
compiled_code = compile(code, "<string>", "exec")
with FakeOutput() as c:
try:
_globals.update({"d": get_device(device_id)})
if is_eval:
ret = eval(code, _globals)
print(">>> " + repr(ret))
else:
exec(compiled_code, _globals)
except:
traceback.print_exc()
return c.output
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-p",
"--port",
type=int,
default=8000,
help="listen port")
args = parser.parse_args()
port = args.port
server = SimpleXMLRPCServer(("0.0.0.0", port), allow_none=True)
server.register_function(run_python_code)
server.register_function(lambda: 'pong', 'ping')
server.register_function(lambda x, y: x + y, 'add')
server.register_function(lambda: os.kill(os.getpid(), signal.SIGTERM),
'quit')
# server.register_function(_connect, "connect")
server.register_multicall_functions()
print(f'Serving XML-RPC on localhost port {port}')
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nKeyboard interrupt received, exiting.")
if __name__ == "__main__":
main()