1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-11 13:55:55 +03:00

XSLT: Use blocking reads instead of a polling thread

This commit is contained in:
AnotherTest 2020-05-30 23:10:10 +04:30
parent 43c2dd50e5
commit a135441218
2 changed files with 102 additions and 108 deletions

View File

@ -196,6 +196,9 @@
<malval kind="function" name="xpath-eval">
<is_macro>false</is_macro>
</malval>
<malval kind="function" name="xslt-halt">
<is_macro>false</is_macro>
</malval>
<!-- evaluate xpath, no context node | requires Saxon PE/EE [paywalls piss me off] -->
</xsl:sequence>
</xsl:function>
@ -599,6 +602,14 @@
<xsl:sequence select="document('xsl_input-string')"/>
</value>
</xsl:when>
<xsl:when test="$func/malval/@name = 'xslt-halt'">
<xsl:message>
<request kind="halt"/>
</xsl:message>
<value>
<malval kind="list" />
</value>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="error(QName('MAL', 'Error'), concat('Invalid function ', $func/malval/@name), core:makeMALValue(concat('Invalid function ', $func/malval/@name), 'string'))"/>
</xsl:otherwise>

View File

@ -28,8 +28,8 @@ try:
except:
pass
finished = False
sem = Lock()
HALT = False
THE_PID = None
init_t = time.time() * 1000
readline_queue = deque()
os.system('rm -rf xsl_error.xml')
@ -40,123 +40,109 @@ def setup_request_file():
os.system('mkfifo xsl_input-string')
def read_nonblocking(path, bufferSize=100, timeout=.1):
grace = True
result = []
pipe = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
try:
while True:
try:
buf = os.read(pipe, bufferSize)
if not buf:
break
else:
content = buf.decode("utf-8")
line = content.split("\n")
result.extend(line)
except OSError as e:
if e.errno == 11 and grace:
# grace period, first write to pipe might take some time
# further reads after opening the file are then successful
time.sleep(timeout)
grace = False
else:
break
def get_one(fd):
s = b""
while True:
x = os.read(fd, 1)
if x == b'\n':
break
if x == b'':
break
s += x
if s == "":
return None
return s.decode('utf-8')
except OSError as e:
if e.errno == errno.ENOENT:
# os.close(pipe)
pipe = None
else:
raise e
os.close(pipe)
return result
def serve_one_request():
res = read_nonblocking('xsl_error.xml', 1024)
def serve_one_request(res):
global HALT
if len(res) == 0:
return
for res in res:
try:
xtree = ET.fromstring("<data>" + res.strip('\x00') + "</data>")
# stdout.write(xtree.attrib['kind'])
for req in xtree:
if req.attrib['kind'] == 'readline':
x = None
if len(readline_queue) > 0:
x = readline_queue.popleft()
else:
x = input(req.attrib['value'])
with open('xsl_input-string', 'w') as fx:
fx.write(x)
# stdout.write(' = ' + x)
elif req.attrib['kind'] == 'display':
stdout.write(req.attrib['value'] + '\n')
elif req.attrib['kind'] == 'time':
x = time.time() * 1000 - init_t
# stdout.write(' = ' + str(int(x)))
with open('xsl_input-string', 'w') as fx:
fx.write(str(int(x)))
# stdout.write('\n')
elif req.attrib['kind'] == 'xpath-eval':
xpath = req.attrib['value']
with open('xsl-eval.xslt', 'w') as f:
f.write(f'<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:env="ENV" xmlns:core="CORE" exclude-result-prefixes="env core xs xsl map fn"><xsl:output omit-xml-declaration="yes"/><xsl:template match="/"><xsl:sequence select="{xpath}" /></xsl:template></xsl:stylesheet>')
with open('xsl-null.xml', 'w') as f:
f.write(req.attrib['context'])
if os.system(f'saxon -xsl:xsl-eval.xslt -s:xsl-null.xml > xsl-eval_output.xml'):
x = ''
else:
with open('xsl-eval_output.xml', 'r') as f:
x = f.read()
with open('xsl_input-string', 'w') as fx:
fx.write(x)
try:
xtree = ET.fromstring("<data>" + res.strip('\x00') + "</data>")
# stdout.write(xtree.attrib['kind'])
for req in xtree:
if req.attrib['kind'] == 'readline':
x = None
if len(readline_queue) > 0:
x = readline_queue.popleft()
else:
stdout.write("UNKNOWN REQUEST " + req.attrib['kind'])
# stdout.write('\n')
except Exception as e:
# if str(e) != 'no element found: line 1, column 0':
# f.seek(0)
# print(e, list(x for x in f.read()))
return
x = input(req.attrib['value'])
with open('xsl_input-string', 'w') as fx:
fx.write(x)
# stdout.write(' = ' + x)
elif req.attrib['kind'] == 'halt':
HALT = True
elif req.attrib['kind'] == 'display':
stdout.write(req.attrib['value'] + '\n')
elif req.attrib['kind'] == 'time':
x = time.time() * 1000 - init_t
# stdout.write(' = ' + str(int(x)))
with open('xsl_input-string', 'w') as fx:
fx.write(str(int(x)))
# stdout.write('\n')
elif req.attrib['kind'] == 'xpath-eval':
xpath = req.attrib['value']
with open('xsl-eval.xslt', 'w') as f:
f.write(f'<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:env="ENV" xmlns:core="CORE" exclude-result-prefixes="env core xs xsl map fn"><xsl:output omit-xml-declaration="yes"/><xsl:template match="/"><xsl:sequence select="{xpath}" /></xsl:template></xsl:stylesheet>')
with open('xsl-null.xml', 'w') as f:
f.write(req.attrib['context'])
if os.system(f'saxon -xsl:xsl-eval.xslt -s:xsl-null.xml > xsl-eval_output.xml'):
x = ''
else:
with open('xsl-eval_output.xml', 'r') as f:
x = f.read()
with open('xsl_input-string', 'w') as fx:
fx.write(x)
else:
stdout.write("UNKNOWN REQUEST " + req.attrib['kind'])
# stdout.write('\n')
except Exception as e:
# if str(e) != 'no element found: line 1, column 0':
# f.seek(0)
# print(e, list(x for x in f.read()))
return
# with open('xsl_error.xml', 'w') as f:
# f.write('')
def serve_requests():
global finished
setup_request_file()
while not finished:
try:
serve_one_request()
except Exception as e:
# print(e)
pass
th = Thread(target=serve_requests)
th.start()
def transform(do_print=True):
global tree
global tree, HALT, THE_PID
tree.write('xslt_input.xml')
if os.system(f'saxon -xsl:"{fname}" -s:xslt_input.xml -TP:perf.html > xslt_output.xml 2> xsl_error.xml'):
return
tree = ET.parse('xslt_output.xml')
if do_print:
stdout = ''
for a in tree.iter('mal'):
for a in a.iter('stdout'):
stdout = a
print(stdout.text)
stdout.clear()
del stdout
setup_request_file()
pid = os.fork()
if pid == 0:
os.system(f'saxon -xsl:"{fname}" -s:xslt_input.xml -TP:perf.html > xslt_output.xml 2> xsl_error.xml')
HALT = True
else:
THE_PID = pid
fd = os.open('xsl_error.xml', os.O_RDONLY | os.O_CLOEXEC)
while True:
try:
if HALT:
os.kill(THE_PID, 9)
raise KeyboardInterrupt()
cmd = get_one(fd)
if cmd:
serve_one_request(cmd)
except KeyboardInterrupt:
exit()
except Exception as e:
print("Harness error:", e)
tree = ET.parse('xslt_output.xml')
if do_print:
stdout = ''
for a in tree.iter('mal'):
for a in a.iter('stdout'):
stdout = a
print(stdout.text)
stdout.clear()
del stdout
if len(args) > 0:
readline_queue.append(f'(load-file "{args0}")')
readline_queue.append(f'(do (load-file "{args0}") (xslt-halt))')
transform(do_print=False)
else:
if fname == 'stepA_mal.xslt':
@ -165,6 +151,3 @@ else:
else:
transform()
readline.write_history_file('.xslt_mal_history')
finished = True
th.join()