From 31237ac54a3dd7828483d1f703dfd39e34d4408b Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Tue, 13 Jan 2015 13:32:26 +0100 Subject: Improve error handling in PrologEngine --- prolog/engine.py | 109 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 25 deletions(-) diff --git a/prolog/engine.py b/prolog/engine.py index 6627b9a..a25a07d 100644 --- a/prolog/engine.py +++ b/prolog/engine.py @@ -2,43 +2,53 @@ import http.client import json +import re import urllib address, port = 'localhost', 3030 class PrologEngine(object): - def __init__(self, address=address, port=port, code=''): - self.id = None + def __init__(self, address=address, port=port, code='', destroy=False, id=None): self.conn = http.client.HTTPConnection(address, port, timeout=10) - hdrs = {'Content-Type': 'application/json; charset=utf-8'} - opts = json.dumps({'destroy': False, 'src_text': code, 'format': 'json'}) - self.conn.request('POST', '/pengine/create', body=opts, headers=hdrs) + # If existing engine ID is given, use it. + if id: + self.id = id + return - response = self.conn.getresponse() - data = response.read() - reply = json.loads(str(data, encoding='utf-8')) - if reply['event'] == 'create': - self.id = reply['id'] - else: - raise Exception('could not create engine: ' + reply['code']) + # Otherwise, create a new engine. + hdrs = {'Content-Type': 'application/json;charset=utf-8'} + opts = json.dumps({'destroy': destroy, 'src_text': code, 'format': 'json-s'}) + reply, outputs = self.request('POST', '/pengine/create', body=opts, headers=hdrs) + + failed = (reply['event'] != 'create') + warnings = [] + errors = [] + for output in outputs: + print(output) + message = PrologEngine.parse_prolog_output(output) + if output['message'] == 'warning': + warnings.append(message) + elif output['message'] == 'error': + failed = True + errors.append(message) + + if failed: + raise Exception('\n'.join(errors)) - def send(self, event, fmt='json'): + self.id = reply['id'] + + def send(self, event): params = urllib.parse.urlencode({ 'id': self.id, 'event': event, - 'format': fmt}) - self.conn.request('GET', '/pengine/send?' + params) - - response = self.conn.getresponse() - data = str(response.read(), encoding='utf-8') - if response.status == http.client.OK: - return json.loads(data) if fmt == 'json' else data - return None - - def ask(self, query, template='', fmt='json'): - event = 'ask(({}),[template({})])'.format(query, template) - reply = self.send(event, fmt=fmt) + 'format': 'json-s'}) + reply, outputs = self.request('GET', '/pengine/send?' + params) + return reply + + def ask(self, query): + event = 'ask(({}),[])'.format(query) + reply = self.send(event) return reply def next(self, n=1): @@ -55,6 +65,55 @@ class PrologEngine(object): self.conn.close() self.conn = None + # Return the main reply and possible output replies. + def request(self, method, path, body=None, headers={}): + self.conn.request(method, path, body, headers) + outputs = [] + while True: + response = self.conn.getresponse() + if response.status != http.client.OK: + raise Exception('server returned {}'.format(response.status)) + reply = json.loads(response.read().decode('utf-8')) + self.id = reply['id'] + if reply['event'] == 'output': + outputs.append(reply) + params = urllib.parse.urlencode({ + 'id': self.id, + 'format': 'json-s'}) + self.conn.request('GET', '/pengine/pull_response?' + params) + else: + return reply, outputs + + # Check if output is an error message and return a prettified version of it. + def parse_prolog_output(output): + match = re.match(r'.*
(.*)
.*', + output['data'], flags=re.DOTALL) + data = match.group(1).strip() + message = '' + if output['message'] == 'error': + if 'location' in output: + loc = output['location'] + message += 'near line ' + str(loc['line']) + if 'ch' in loc: + message += ', character ' + str(loc['ch']) + message += ': ' + + if output.get('code') == 'syntax_error': + match = re.match(r'^.*Syntax error: (.*)$', data, flags=re.DOTALL) + message += match.group(1) + elif output.get('code') == 'permission_error': + match = re.match(r'^.*(No permission [^\n]*)', data, flags=re.DOTALL) + message += match.group(1) + elif output.get('code') == 'type_error': + match = re.match(r'^.*(Type error: [^\n]*)', data, flags=re.DOTALL) + message += match.group(1) + else: + message += data + + # Replace anonymous variable names with _. + message = re.sub(r'_G[0-9]*', '_', message) + return message + # Basic sanity check. if __name__ == '__main__': engine = PrologEngine(code='dup([],[]). dup([H|T],[H,H|TT]) :- dup(T,TT).') -- cgit v1.2.1