8000 Rewrite response validation system · neumond/python-computer-craft@5c00e80 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5c00e80

Browse files
committed
Rewrite response validation system
1 parent 5438ecb commit 5c00e80

23 files changed

+492
-313
lines changed

computercraft/back.lua

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,23 @@ while true do
2222
msg = textutils.unserializeJSON(p2)
2323
if msg.action == 'task' then
2424
local fn, err = loadstring(msg.code)
25-
if fn ~= nil then
26-
setfenv(fn, genv)
27-
tasks[msg.task_id] = coroutine.create(fn)
28-
else
25+
if fn == nil then
2926
ws.send(textutils.serializeJSON{
3027
action='task_result',
3128
task_id=msg.task_id,
3229
result={false, err},
3330
})
31+
else
32+
setfenv(fn, genv)
33+
if msg.immediate then
34+
ws.send(textutils.serializeJSON{
35+
action='task_result',
36+
task_id=msg.task_id,
37+
result={fn()},
38+
})
39+
else
40+
tasks[msg.task_id] = coroutine.create(fn)
41+
end
3442
end
3543
elseif msg.action == 'sub' then
3644
event_sub[msg.event] = true

computercraft/rproc.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from .errors import LuaException
2+
3+
4+
def coro(result):
5+
assert isinstance(result, list)
6+
if len(result) < 2:
7+
result.append(None)
8+
assert len(result) == 2
9+
success, result = result
10+
assert isinstance(success, bool)
11+
if not success:
12+
raise LuaException(result)
13+
return result
14+
15+
16+
def nil(result):
17+
assert result is None
18+
return result
19+
20+
21+
def boolean(result):
22+
assert result is True or result is False
23+
return result
24+
25+
26+
def integer(result):
27+
assert isinstance(result, int)
28+
assert not isinstance(result, bool)
29+
return result
30+
31+
32+
def number(result):
33+
assert isinstance(result, (int, float))
34+
assert not isinstance(result, bool)
35+
return result
36+
37+
38+
def string(result):
39+
assert isinstance(result, str)
40+
return result
41+
42+
43+
def any_dict(result):
44+
assert isinstance(result, dict)
45+
return result
46+
47+
48+
def any_list(result):
49+
if result == {}:
50+
result = []
51+
assert isinstance(result, list)
52+
return result
53+
54+
55+
def fact_tuple(*components):
56+
def proc(result):
57+
result = any_list(result)
58+
assert len(components) == result
59+
return tuple(comp(value) for comp, value in zip(components, result))
60+
return proc
61+
62+
63+
def fact_array(component):
64+
def proc(result):
65+
result = any_list(result)
66+
return [component(value) for value in result]
67+
return proc
68+
69+
70+
def fact_option(component):
71+
def proc(result):
72+
if result is None:
73+
return None
74+
return component(result)
75+
return proc
76+
77+
78+
def fact_union(*case_pairs, pelse):
79+
def proc(result):
80+
for detector, processor in case_pairs:
81+
if detector(result):
82+
return processor(result)
83+
return pelse(result)
84+
return proc
85+
86+
87+
def fact_scheme_dict(components):
88+
def proc(result):
89+
result = any_dict(result)
90+
assert set(result.keys()) == set(components.keys())
91+
return {key: component(result[key]) for key, component in components.items()}
92+
return proc
93+
94+
95+
def fact_scheme_dict_kw(**components):
96+
return fact_scheme_dict(components)
97+
98+
99+
def fact_mono_dict(key, value):
100+
def proc(result):
101+
result = any_dict(result)
102+
return {key(k): value(v) for k, v in result.items()}
103+
return proc
104+
105+
106+
tuple3_number = fact_tuple(number, number, number)
107+
tuple2_integer = fact_tuple(integer, integer)
108+
tuple3_integer = fact_tuple(integer, integer, integer)
109+
array_integer = fact_array(integer)
110+
array_string = fact_array(string)
111+
option_integer = fact_option(integer)
112+
option_string = fact_option(string)

computercraft/server.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import argparse
99

1010
from .subapis.root import RootAPIMixin
11-
from .errors import LuaException
11+
from . import rproc
1212

1313
from .subapis.colors import ColorsAPI
1414
from .subapis.commands import CommandsAPI
@@ -101,24 +101,26 @@ def _new_task_id(self) -> str:
101101
self._task_autoid += 1
102102
return task_id
103103

104-
async def raw_eval(self, lua_code):
104+
async def raw_eval(self, lua_code, immediate=False):
105105
task_id = self._new_task_id()
106106
self._result_locks[task_id] = asyncio.Event()
107107
await self._cmd.put({
108108
'action': 'task',
109109
'task_id': task_id,
110110
'code': lua_code,
111+
'immediate': immediate,
111112
})
112113
await self._result_locks[task_id].wait()
113114
del self._result_locks[task_id]
114-
return self._result_values.pop(task_id)
115+
result = self._result_values.pop(task_id)
116+
print('{} → {}'.format(lua_code, repr(result)))
117+
return result
118+
119+
async def raw_eval_coro(self, lua_code):
120+
return rproc.coro(await self.raw_eval(lua_code))
115121

116122
async def _send_cmd(self, lua):
117-
result = await self.raw_eval(lua)
118-
print('{} → {}'.format(lua, result))
119-
if not result[0]:
120-
raise LuaException(*result[1:])
121-
return result[1:]
123+
return await self.raw_eval_coro(lua)
122124

123125
async def _start_queue(self, event):
124126
task_id = self._new_task_id()
@@ -173,6 +175,12 @@ async def _launch_program(self, ws):
173175
'error': 'protocol error',
174176
})
175177
return None
178+
if len(msg['args']) == 0:
179+
await ws.send_json({
180+
'action': 'close',
181+
'error': 'arguments required',
182+
})
183+
return None
176184
module = import_module(self['source_module'])
177185
program = getattr(module, msg['args'][0], None)
178186
if program is None:

computercraft/subapis/colors.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Tuple
22

3-
from .base import BaseSubAPI, bool_return, int_return
3+
from .base import BaseSubAPI
4+
from ..rproc import boolean, integer, tuple3_number
45

56

67
class ColorsAPI(BaseSubAPI):
@@ -43,23 +44,16 @@ class ColorsAPI(BaseSubAPI):
4344
}
4445

4546
async def combine(self, *colors: int) -> int:
46-
return int_return(await self._send('combine', *colors))
47+
return integer(await self._send('combine', *colors))
4748

4849
async def subtract(self, color_set: int, *colors: int) -> int:
49-
return int_return(await self._send('subtract', color_set, *colors))
50+
return integer(await self._send('subtract', color_set, *colors))
5051

5152
async def test(self, colors: int, color: int) -> bool:
52-
return bool_return(await self._send('test', colors, color))
53+
return boolean(await self._send('test', colors, color))
5354

5455
async def packRGB(self, r: float, g: float, b: float) -> int:
55-
return int_return(await self._send('packRGB', r, g, b))
56+
return integer(await self._send('packRGB', r, g, b))
5657

5758
async def unpackRGB(self, rgb: int) -> Tuple[float, float, float]:
58-
ret = await self._send('unpackRGB', rgb)
59-
assert isinstance(ret, list)
60-
assert len(ret) == 3
61-
return tuple(ret)
62-
63-
async def rgb8(self, *args):
64-
r = await self._send('rgb8', *args)
65-
return r[0] if len(r) == 1 else r
59+
return tuple3_number(await self._send('unpackRGB', rgb))

computercraft/subapis/commands.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
from typing import Tuple, List, Optional
2-
from .base import BaseSubAPI, int_return, list_return, dict_return
2+
3+
from .base import BaseSubAPI
34
from ..errors import CommandException
5+
from ..rproc import tuple3_integer, any_dict, any_list, array_string, integer
46

57

68
class CommandsAPI(BaseSubAPI):
79
_API = 'commands'
810

911
async def exec(self, command: str) -> Tuple[str, Optional[int]]:
12+
# TODO: use rproc
1013
r = await self._send('exec', command)
1114
assert len(r) == 3
1215
assert isinstance(r[0], bool)
@@ -26,19 +29,16 @@ async def exec(self, command: str) -> Tuple[str, Optional[int]]:
2629
return r[1], r[2]
2730

2831
async def execAsync(self, command: str) -> int:
29-
return int_return(await self._send('execAsync', command))
32+
return integer(await self._send('execAsync', command))
3033

3134
async def list(self) -> List[str]:
32-
return list_return(await self._send('list'))
35+
return array_string(await self._send('list'))
3336

3437
async def getBlockPosition(self) -> Tuple[int, int, int]:
35-
ret = await self._send('getBlockPosition')
36-
assert isinstance(ret, list)
37-
assert len(ret) == 3
38-
return tuple(ret)
38+
return tuple3_integer(await self._send('getBlockPosition'))
3939

4040
async def getBlockInfo(self, x: int, y: int, z: int) -> dict:
41-
return dict_return(await self._send('getBlockInfo', x, y, z))
41+
return any_dict(await self._send('getBlockInfo', x, y, z))
4242

4343
async def getBlockInfos(self, x1: int, y1: int, z1: int, x2: int, y2: int, z2: int) -> List[dict]:
44-
return list_return(await self._send('getBlockInfos', x1, y1, z1, x2, y2, z2))
44+
return any_list(await self._send('getBlockInfos', x1, y1, z1, x2, y2, z2))

computercraft/subapis/disk.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
11
from typing import Optional, Union
2-
from .base import BaseSubAPI, nil_return, bool_return, opt_str_return, opt_int_return, opt_str_bool_return
2+
3+
from .base import BaseSubAPI
4+
from ..rproc import boolean, string, nil, option_integer, option_string, fact_option, fact_union
5+
6+
7+
option_string_bool = fact_option(fact_union(
8+
(
9+
lambda v: v is True or v is False,
10+
boolean,
11+
),
12+
pelse=string,
13+
))
314

415

516
class DiskAPI(BaseSubAPI):
617
_API = 'disk'
718

819
async def isPresent(self, side: str) -> bool:
9-
return bool_return(await self._send('isPresent', side))
20+
return boolean(await self._send('isPresent', side))
1021

1122
async def hasData(self, side: str) -> bool:
12-
return bool_return(await self._send('hasData', side))
23+
return boolean(await self._send('hasData', side))
1324

1425
async def getMountPath(self, side: str) -> Optional[str]:
15-
return opt_str_return(await self._send('getMountPath', side))
26+
return option_string(await self._send('getMountPath', side))
1627

1728
async def setLabel(self, side: str, label: str):
18-
return nil_return(await self._send('setLabel', side, label))
29+
return nil(await self._send('setLabel', side, label))
1930

2031
async def getLabel(self, side: str) -> Optional[str]:
21-
return opt_str_return(await self._send('getLabel', side))
32+
return option_string(await self._send('getLabel', side))
2233

2334
async def getID(self, side: str) -> Optional[int]:
24-
return opt_int_return(await self._send('getID', side))
35+
return option_integer(await self._send('getID', side))
2536

2637
async def hasAudio(self, side: str) -> bool:
27-
return bool_return(await self._send('hasAudio', side))
38+
return boolean(await self._send('hasAudio', side))
2839

2940
async def getAudioTitle(self, side: str) -> Optional[Union[bool, str]]:
30-
return opt_str_bool_return(await self._send('getAudioTitle', side))
41+
return option_string_bool(await self._send('getAudioTitle', side))
3142

3243
async def playAudio(self, side: str):
33-
return nil_return(await self._send('playAudio', side))
44+
return nil(await self._send('playAudio', side))
3445

3546
async def stopAudio(self, side: str):
36-
return nil_return(await self._send('stopAudio', side))
47+
return nil(await self._send('stopAudio', side))
3748

3849
async def eject(self, side: str):
39-
return nil_return(await self._send('eject', side))
50+
return nil(await self._send('eject', side))

0 commit comments

Comments
 (0)
0