Skip to content

Commit

Permalink
Add abort functionality and handle exception in cleanup (#150)
Browse files Browse the repository at this point in the history
* Try-except cleanup Prolog
* Feature: Add abort method to Prolog class
  • Loading branch information
g-gemignani authored Jul 13, 2024
1 parent 8b002d4 commit 4e8dc5c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
6 changes: 5 additions & 1 deletion pyswip/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1367,5 +1367,9 @@ def cleanupProlog():
# TODO Prolog documentation says cleanup with code 0 may be interrupted
# If the program has come to an end the prolog system should not
# interfere with that. Therefore we may want to use 1 instead of 0.
PL_cleanup(int(_hook.exit_code or 0))
try:
exit_code = int(_hook.exit_code or 0)
except ValueError:
exit_code = 0
PL_cleanup(exit_code)
_isCleaned = True
13 changes: 11 additions & 2 deletions pyswip/prolog.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,19 @@ class Prolog:

# We keep track of open queries to avoid nested queries.
_queryIsOpen = False
_queryWrapper = None

class _QueryWrapper(object):

def __init__(self):
self._swipl_qid = None
self._swipl_fid = None
if Prolog._queryIsOpen:
raise NestedQueryError("The last query was not closed")

def __call__(self, query, maxresult, catcherrors, normalize):
Prolog._init_prolog_thread()
swipl_fid = PL_open_foreign_frame()
self._swipl_fid = PL_open_foreign_frame()

swipl_head = PL_new_term_ref()
swipl_args = PL_new_term_refs(2)
Expand Down Expand Up @@ -143,6 +146,11 @@ def _init_prolog_thread(cls):
elif pengine_id == -2:
print("{WARN} Single-threaded swipl build, beware!")

@classmethod
def abort(cls):
# The clean_up method is called to make sure that no query is still running
cls._queryWrapper.clean_up()

@classmethod
def asserta(cls, assertion, catcherrors=False):
next(cls.query(assertion.join(["asserta((", "))."]), catcherrors=catcherrors))
Expand Down Expand Up @@ -183,7 +191,8 @@ def query(cls, query, maxresult=-1, catcherrors=True, normalize=True):
>>> print sorted(prolog.query("father(michael,X)"))
[{'X': 'gina'}, {'X': 'john'}]
"""
return cls._QueryWrapper()(query, maxresult, catcherrors, normalize)
cls._queryWrapper = cls._QueryWrapper()
return cls._queryWrapper(query, maxresult, catcherrors, normalize)


def normalize_values(values):
Expand Down
35 changes: 35 additions & 0 deletions tests/test_prolog.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,38 @@ def test_prolog_read_file(self):
prolog = pl.Prolog()
prolog.consult("tests/test_read.pl")
list(prolog.query('read_file("tests/test_read.pl", S)'))

def test_abort_query(self):
"""
SWI-Prolog cannot have nested queries called by the foreign function
interface, that is, if we open a query and are getting results from it,
we cannot open another query before closing that one.
However, the interface allows to interrupt a query via the abort method.
This test makes sure that this feature works correctly.
"""
p = pl.Prolog()

# Add something to the base
p.assertz("father(john,mich)")
p.assertz("father(john,gina)")
p.assertz("mother(jane,mich)")

somequery = "father(john, Y)"
otherquery = "mother(jane, X)"


# This should not throw an exception
for q in p.query(somequery):
p.abort()
for j in p.query(otherquery):
# This should not throw an error, because we aborted the previous query
pass

# But this one should
with self.assertRaises(pl.NestedQueryError):
for q in p.query(somequery):
for j in p.query(otherquery):
# This should throw an error, because I opened the second
# query
pass

0 comments on commit 4e8dc5c

Please sign in to comment.