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:
parent
43c2dd50e5
commit
a135441218
@ -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>
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user