Module swiplserver.prologmqi
Allows using SWI Prolog as an embedded part of an application, "like a library".
swiplserver
enables SWI Prolog queries to be executed from within your Python application as if Python had a Prolog engine running inside of it. Queries are sent as strings like "atom(foo)" and the response is JSON.
swiplserver
provides:
- The
PrologMQI
class that automatically manages starting and stopping a SWI Prolog instance and starts the Machine Query Interface ('MQI') using themqi_start/1
predicate to allow running Prolog queries. - The
PrologThread
class is used to run queries on the created process. Queries are run exactly as they would be if you were interacting with the SWI Prolog "top level" (i.e. the Prolog command line).
Installation
- Install SWI Prolog (www.swi-prolog.org) and ensure that "swipl" is on the system path.
- Either "pip install swiplserver" or copy the "swiplserver" library (the whole directory) from the "libs" directory of your SWI Prolog installation to be a subdirectory of your Python project.
- Check if your SWI Prolog version includes the Machine Query Interface by launching it and typing
?- mqi_start([]).
If it can't find it, see below for how to install it.
If your SWI Prolog doesn't yet include the Machine Query Interface:
- Download the
mqi.pl
file from the GitHub repository. - Open an operating system command prompt and go to the directory where you downloaded
mqi.pl
. - Run the below command. On Windows the command prompt must be run as an administrator. On Mac or Linux, start the command with
sudo
as insudo swipl -s ...
.
swipl -s mqi.pl -g "mqi:install_to_library('mqi.pl')" -t halt
Usage
PrologThread
represents a thread in Prolog (it is not a Python thread!). A given PrologThread
instance will always run queries on the same Prolog thread (i.e. it is single threaded within Prolog).
To run a query and wait until all results are returned:
from swiplserver import PrologMQI, PrologThread
with PrologMQI() as mqi:
with mqi.create_thread() as prolog_thread:
result = prolog_thread.query("atom(a)")
print(result)
True
To run a query that returns multiple results and retrieve them as they are available:
from swiplserver import PrologMQI, PrologThread
with PrologMQI() as mqi:
with mqi.create_thread() as prolog_thread:
prolog_thread.query_async("member(X, [first, second, third])",
find_all=False)
while True:
result = prolog_thread.query_async_result()
if result is None:
break
else:
print(result)
first
second
third
Creating two PrologThread
instances allows queries to be run on multiple threads in Prolog:
from swiplserver import PrologMQI, PrologThread
with PrologMQI() as mqi:
with mqi.create_thread() as prolog_thread1:
with mqi.create_thread() as prolog_thread2:
prolog_thread1.query_async("sleep(2), writeln(first_thread(true))")
prolog_thread2.query_async("sleep(1), writeln(second_thread(true))")
thread1_answer = prolog_thread1.query_async_result()
thread2_answer = prolog_thread2.query_async_result()
Prolog: second_thread(true)
Prolog: first_thread(true)
Output printed in Prolog using writeln/1
or errors output by Prolog itself are written to Python's logging facility using the swiplserver
log and shown prefixed with "Prolog:" as above.
Answers to Prolog queries that are not simply True
or False
are converted to JSON using the json_to_prolog/2 predicate in Prolog. They are returned as a Python dict
with query variables as the keys and standard JSON as the values. If there is more than one answer, it is returned as a list:
from swiplserver import PrologMQI, PrologThread
with PrologMQI() as mqi:
with mqi.create_thread() as prolog_thread:
result = prolog_thread.query("member(X, [color(blue), color(red)])")
print(result)
[{'X': {'functor': 'color', 'args': ['blue']}},
{'X': {'functor': 'color', 'args': ['red']}}]
Exceptions in Prolog code are raised using Python's native exception facilities.
Debugging
When using swiplserver
, debugging the Prolog code itself can often be done by viewing traces from the Prolog native writeln/1
or debug/3
predicates and viewing their output in the debugger output window.
Sometimes an issue occurs deep in an application and it would be easier to set breakpoints and view traces in Prolog itself. Running SWI Prolog manually and launching the Machine Query Interface in "Standalone mode" is designed for this scenario.
swiplserver
normally launches SWI Prolog and starts the Machine Query Interface so that it can connect and run queries. To debug your code using Prolog itself, you can do this manually and connect your application to it. A typical flow for standalone mode is:
- Launch SWI Prolog and call the
mqi_start/1
predicate, specifying a port and password (documentation is here). Use thetdebug/0
predicate to set all threads to debugging mode like this:tdebug, mqi_start([port(4242), password(debugnow)])
. - Optionally run the predicate
debug(mqi(_)).
in Prolog to turn on tracing for the Machine Query Interface. - Set the selected port and password when you call
PrologMQI
. - Launch the application and go through the steps to reproduce the issue.
(As the Machine Query Interface is multithreaded, debugging the running code requires using the multithreaded debugging features of SWI Prolog as described in the section on "Debugging Threads" in the SWI Prolog documentation.)
At this point, all of the multi-threaded debugging tools in SWI Prolog are available for debugging the problem. If the issue is an unhandled or unexpected exception, the exception debugging features of SWI Prolog can be used to break on the exception and examine the state of the application. If it is a logic error, breakpoints can be set to halt at the point where the problem appears, etc.
Note that, while using a library to access Prolog will normally end and restart the process between runs of the code, running the Machine Query Interface standalone won't. You'll either need to relaunch between runs or build your application so that it does the initialization at startup.
Functions
def create_posix_path(os_path)
-
Convert a file path in whatever the current OS path format is to be a posix path so Prolog can understand it.
This is useful for Prolog predicates like
consult
which need a Posix path to be passed in on any platform. def is_prolog_atom(json_term)
-
True if json_term is Prolog JSON representing a Prolog atom. See
PrologThread.query()
for documentation on the Prolog JSON format. def is_prolog_functor(json_term)
-
True if json_term is Prolog JSON representing a Prolog functor (i.e. a term with zero or more arguments). See
PrologThread.query()
for documentation on the Prolog JSON format. def is_prolog_list(json_term)
-
True if json_term is Prolog JSON representing a Prolog list. See
PrologThread.query()
for documentation on the Prolog JSON format. def is_prolog_variable(json_term)
-
True if json_term is Prolog JSON representing a Prolog variable. See
PrologThread.query()
for documentation on the Prolog JSON format. def json_to_prolog(json_term)
-
Convert json_term from the Prolog JSON format to a string that represents the term in the Prolog language. See
PrologThread.query()
for documentation on the Prolog JSON format. def prolog_args(json_term)
-
Return the arguments from json_term if json_term is in the Prolog JSON format. See
PrologThread.query()
for documentation on the Prolog JSON format. def prolog_name(json_term)
-
Return the atom (if json_term is an atom), variable (if a variable) or functor name of json_term. json_term must be in the Prolog JSON format. See
PrologThread.query()
for documentation on the Prolog JSON format. def quote_prolog_identifier(identifier: str)
-
Surround a Prolog identifier with '' if Prolog rules require it.
Classes
class PrologConnectionFailedError (exception_json)
-
Raised when the connection used by a
PrologThread
fails. Indicates that the Machine Query Interface will no longer respond.Ancestors
- PrologError
- builtins.Exception
- builtins.BaseException
Inherited members
class PrologError (exception_json)
-
Base class used for all exceptions raised by
swiplserver.PrologMQI
except for PrologLaunchError. Used directly when an exception is thrown by Prolog code itself, otherwise the subclass exceptions are used.Ancestors
- builtins.Exception
- builtins.BaseException
Subclasses
- PrologConnectionFailedError
- PrologNoQueryError
- PrologQueryCancelledError
- PrologQueryTimeoutError
- PrologResultNotAvailableError
Methods
def is_prolog_exception(self, term_name)
-
True if the exception thrown by Prolog code has the term name specified by term_name.
Args
term_name
- The name of the Prolog term to test for.
def json(self)
-
Returns
A string that represents the Prolog exception in Prolog json form. See
PrologThread.query()
for documentation on the Prolog json format. def prolog(self)
-
Returns
A string that represents the Prolog exception in the Prolog native form.
class PrologLaunchError (*args, **kwargs)
-
Raised when the SWI Prolog process was unable to be launched for any reason. This can include a version mismatch between the library and the server.
Ancestors
- builtins.Exception
- builtins.BaseException
class PrologMQI (launch_mqi: bool = True, port: int = None, password: str = None, unix_domain_socket: str = None, query_timeout_seconds: float = None, pending_connection_count: int = None, output_file_name: str = None, mqi_traces: str = None, prolog_path: str = None, prolog_path_args: list = None)
-
Initialize a PrologMQI class that manages a SWI Prolog process associated with your application process.
PrologMQI.start()
actually launches the process if launch_mqi is True.This class is designed to allow Prolog to be used "like a normal Python library" using the Machine Query Interface of SWI Prolog. All communication is done using protocols that only work on the same machine as your application (localhost TCP/IP or Unix Domain Sockets), and the implementation is designed to make sure the process doesn't hang around even if the application is terminated unexpectedly (as with halting a debugger).
All arguments are optional and the defaults are set to the recommended settings that work best on all platforms during development. In production on Unix systems, consider using unix_domain_socket to further decrease security attack surface area.
For debugging scenarios, SWI Prolog can be launched manually and this class can be configured to (locally) connect to it using launch_mqi = False. This allows for inspection of the Prolog state and usage of the SWI Prolog debugging tools while your application is running. See the documentation for the Prolog
mqi_start/1
predicate for more information on how to run the Machine Query Interface in "Standalone Mode".Examples
To automatically launch a SWI Prolog process using TCP/IP localhost and an automatically chosen port and password (the default):
with PrologMQI() as mqi: # your code here
To connect to an existing SWI Prolog process that has already started the
mqi_start/1
predicate and is using an automatically generated Unix Domain Socket (this value will be different for every launch) and a password of '8UIDSSDXLPOI':with PrologMQI(launch_mqi = False, unix_domain_socket = '/tmp/swipl_udsock_15609_1/swipl_15609_2', password = '8UIDSSDXLPOI') as mqi: # your code here
Args
launch_mqi
- True (default) launch a SWI Prolog process on
PrologMQI.start()
and shut it down automatically onPrologMQI.stop()
(or after a resource manager like the Python "with" statement exits). False connects to an existing SWI Prolog process that is running themqi_start/1
predicate (i.e. "Standalone Mode"). When False,password
and one ofport
orunix_domain_socket
must be specified to match the options provided tomqi_start/1
in the separate SWI Prolog process. port
-
The TCP/IP localhost port to use for communication with the SWI Prolog process. Ignored if
unix_domain_socket
is not None.- When
launch_mqi
is True, None (default) automatically picks an open port that the Machine Query Interface and this class both use. - When
launch_mqi
is False, must be set to match the port specified inmqi_start/1
of the running SWI Prolog process.
- When
password
-
The password to use for connecting to the SWI Prolog process. This is to prevent malicious users from connecting to the Machine Query Interface since it can run arbitrary code. Allowing the MQI to generate a strong password by using None is recommended.
- When
launch_mqi
is True, None (default) automatically generates a strong password using a uuid. Other values specify the password to use. - When
launch_mqi
is False, must be set to match the password specified inmqi_start/1
of the running SWI Prolog process.
- When
unix_domain_socket
-
None (default) use localhost TCP/IP for communication with the SWI Prolog process. Otherwise (only on Unix) is either a fully qualified path and filename of the Unix Domain Socket to use or an empty string (recommended). An empty string will cause a temporary directory to be created using Prolog's
tmp_file/2
and a socket file will be created within that directory following the below requirements. If the directory and file are unable to be created for some reason,PrologMQI.start()
with raise an exception. Specifying a file to use should follow the same guidelines as the generated file:- If the file exists when the Machine Query Interface is launched, it will be deleted.
- The Prolog process will attempt to create and, if Prolog exits cleanly, delete this file when the Machine Query Interface closes. This means the directory must have the appropriate permissions to allow the Prolog process to do so.
- For security reasons, the filename should not be predictable and the directory it is contained in should have permissions set so that files created are only accessible to the current user.
- The path must be below 92 bytes long (including null terminator) to be portable according to the Linux documentation.
query_timeout_seconds
- None (default) set the default timeout for all queries to be infinite (this can be changed on a per query basis). Other values set the default timeout in seconds.
pending_connection_count
-
Set the default number of pending connections allowed on the Machine Query Interface. Since the MQI is only connected to by your application and is not a server, this value should probably never be changed unless your application is creating new
PrologThread
objects at a very high rate.- When
launch_mqi
is True, None uses the default (5) and other values set the count. - When
launch_mqi
is False, ignored.
- When
output_file_name
- Provide the file name for a file to redirect all Prolog output (STDOUT and STDERR) to. Used for debugging or gathering a log of Prolog output. None outputs all Prolog output to the Python logging infrastructure using the 'swiplserver' log. If using multiple Machine Query Interfaces in one SWI Prolog instance, only set this on the first one. Each time it is set the output will be deleted and redirected.
mqi_traces
-
(Only used in unusual debugging circumstances) Since these are Prolog traces, where they go is determined by
output_file_name
.- None (the default) does not turn on mqi tracing
- "_" turns on all tracing output from the Prolog Machine Query Interface (i.e. runs
debug(mqi(_)).
in Prolog). - "protocol" turns on only protocol level messages (which results in much less data in the trace for large queries)
- "query" turns on only messages about the query.
prolog_path
- (Only used for unusual testing situations) Set the path to where the swipl executable can be found.
prolog_path_args
- (Only used for unusual testing situations) Set extra command line arguments to be sent to swipl when it is launched.
Raises
ValueError if the arguments don't make sense. For example: choosing Unix Domain Sockets on Windows or setting output_file with launch_mqi = False
Static methods
def unix_domain_socket_file(directory: str)
-
Creates a non-predictable Filename 36 bytes long suitable for using in the unix_domain_socket argument of the
PrologMQI
constructor. Appends it to directory.Note that Python's gettempdir() function generates paths which are often quite large on some platforms and thus (at the time of this writing) is not suitable for use as the directory. The recommendation is to create a custom directory in a suitably short path (see notes below on length) in the filesystem and use that as directory. Ensure that the permissions for this folder are set as described below.
Args
directory
-
The fully qualified directory the file name will be appended to. Note that:
- The directory containing the file must grant the user running the application (and ideally only that user) the ability to create and delete files created within it.
- The total path (including the 36 bytes used by the file) must be below 92 bytes long (including null terminator) to be portable according to the Linux documentation.
Returns
A fully qualified path to a file in directory.
Methods
def create_thread(self)
-
Create a new
PrologThread
instance for thisPrologMQI
.Examples
Using with the Python
with
statement is recommended:with PrologMQI() as mqi: with mqi.create_thread() as prolog_thread: # Your code here
Returns
A
PrologThread
instance. def process_id(self)
-
Retrieve the operating system process id of the SWI Prolog process that was launched by this class.
Returns
None if the value of
launch_mqi
passed toPrologMQI
is False or ifPrologMQI.start()
has not yet been called. Otherwise return the operating system process ID. def start(self)
-
Start a new SWI Prolog process associated with this class using the settings from
PrologMQI
and start the Machine Query Interface using themqi_start
Prolog predicate. Iflaunch_mqi
is False, does nothing.To create the SWI Prolog process, 'swipl' must be on the system path. Manages the lifetime of the process it creates, ending it on
PrologMQI.stop()
.Raises
PrologLaunchError
- The SWI Prolog process was unable to be launched. Often indicates that
swipl
is not in the system path.
def stop(self, kill=False)
-
Stop the SWI Prolog process and wait for it to exit if it has been launched by using
launch_mqi = True
onPrologMQI
creation.Does nothing if
launch_mqi
is False.Args
kill
- False (default) connect to the Machine Query Interface and ask it to perform an orderly shutdown of Prolog and exit the process. True uses the Python subprocess.kill() command which will terminate it immediately. Note that if PrologMQI.connection_failed is set to true (due to a failure that indicates the MQI will not respond), subprocess.kill() will be used regardless of this setting.
class PrologNoQueryError (exception_json)
-
Raised by
PrologThread.cancel_query_async()
andPrologThread.query_async_result()
if there is no query running and no results to retrieve.Ancestors
- PrologError
- builtins.Exception
- builtins.BaseException
Inherited members
class PrologQueryCancelledError (exception_json)
-
Raised by
PrologThread.query_async_result()
when the query has been cancelled.Ancestors
- PrologError
- builtins.Exception
- builtins.BaseException
Inherited members
class PrologQueryTimeoutError (exception_json)
-
Raised when a Prolog query times out when calling
PrologThread.query()
orPrologThread.query_async()
with a timeout.Ancestors
- PrologError
- builtins.Exception
- builtins.BaseException
Inherited members
class PrologResultNotAvailableError (exception_json)
-
Raised by
PrologThread.query_async_result()
when the next result to a query is not yet available.Ancestors
- PrologError
- builtins.Exception
- builtins.BaseException
Inherited members
class PrologThread (prolog_mqi: PrologMQI)
-
Initialize a PrologThread instance for running Prolog queries on a single, consistent thread in the Machine Query Interface managed by
prolog_mqi
(does not create a thread in Python).Each
PrologThread
class represents a single, consistent thread inprolog_mqi
that can run queries usingPrologThread.query()
orPrologThread.query_async()
. Queries on a singlePrologThread
will never run concurrently.However, running queries on more than one
PrologThread
instance will run concurrent Prolog queries and all the multithreading considerations that that implies.Usage
All of these are equivalent and automatically start SWI Prolog and the Machine Query Interface:
PrologThread instances can be created and started manually:
mqi = PrologMQI() prolog_thread = PrologThread(mqi) prolog_thread.start() # Your code here
Or (recommended) started automatically using the Python
with
statement:with PrologMQI() as mqi: with PrologThread(mqi) as prolog_thread: # Your code here
Or using the handy helper function:
with PrologMQI() as mqi: with mqi.create_thread() as prolog_thread: # Your code here
Methods
def cancel_query_async(self)
-
Attempt to cancel a query started with
PrologThread.query_async()
in a way that allows further queries to be run on thisPrologThread
afterwards.If there is a query running, injects a Prolog
throw(cancel_goal)
into the query's thread. Does not inject Prologabort/0
because this would kill the thread and we want to keep the thread alive for future queries. This means it is a "best effort" cancel since the exception can be caught by your Prolog code.cancel_query_async()
is guaranteed to either raise an exception (if there is no query or pending results from the last query), or safely attempt to stop the last executed query.To guaranteed that a query is cancelled, call
PrologThread.stop()
instead.It is not necessary to determine the outcome of
cancel_query_async()
after calling it. Further queries can be immediately run after callingcancel_query_async()
. They will be run after the current query stops for whatever reason.If you do need to determine the outcome or determine when the query stops, call
PrologThread.query_async_result(wait_timeout_seconds = 0)
. Usingwait_timeout_seconds = 0
is recommended since the query might have caught the exception or still be running. CallingPrologThread.query_async_result()
will return the "natural" result of the goal's execution. The "natural" result depends on the particulars of what the code actually did. The return value could be one of:- Raise
PrologQueryCancelledError
if the goal was running and did not catch the exception. I.e. the goal was successfully cancelled. - Raise
PrologQueryTimeoutError
if the query timed out before getting cancelled. - Raise
PrologError
(i.e. an arbitrary exception) if query hits another exception before it has a chance to be cancelled. - A valid answer if the query finished before being cancelled.
Note that you will need to continue calling
PrologThread.query_async_result()
until you receiveNone
or an exception to be sure the query is finished (see documentation forPrologThread.query_async_result()
).Raises
PrologNoQueryError
if there was no query running and no results that haven't been retrieved yet from the last query.PrologConnectionFailedError
if the query thread has unexpectedly exited. The MQI will no longer be listening after this exception.Returns
True
. Note that this does not mean the query was successfully cancelled (see notes above). - Raise
def halt_server(self)
-
Perform an orderly shutdown of the Machine Query Interface and end the Prolog process.
This is called automatically by
PrologMQI.stop()
and when aPrologMQI
instance is used in a Pythonwith
statement. def query(self, value: str, query_timeout_seconds: float = None)
-
Run a Prolog query and wait to return all results (as if run using Prolog
findall/3
) or optionally time out.Calls
PrologMQI.start()
andPrologThread.start()
if either is not already started.The query is run on the same Prolog thread every time, emulating the Prolog top level. There is no way to cancel the goal using this method, so using a timeout is recommended. To run a cancellable goal, use
PrologThread.query_async()
.Args
value
- A Prolog query to execute as a string, just like you would run on the Prolog top level. e.g.
"member(X, [1, 2]), X = 2"
. query_timeout_seconds
None
uses the query_timeout_seconds set in the prolog_mqi object passed toPrologThread
.
Raises
PrologQueryTimeoutError
if the query timed out.PrologError
for all other exceptions that occurred when running the query in Prolog.PrologConnectionFailedError
if the query thread has unexpectedly exited. The MQI will no longer be listening after this exception.Returns
False
- The query failed.
True
- The query succeeded once with no free variables.
list
-
The query succeeded once with free variables or more than once with no free variables. There will be an item in the list for every answer. Each item will be:
True
if there were no free variables- A
dict
if there were free variables. Each key will be the name of a variable, each value will be the JSON representing the term it was unified with. Note that a special variable called$residuals
will be added to each answer that has residual variable constraints on it. This will contain a list of all the constraints on all the variables for that answer.
def query_async(self, value: str, find_all: bool = True, query_timeout_seconds: float = None)
-
Start a Prolog query and return immediately unless a previous query is still running. In that case, wait until the previous query finishes before returning.
Calls
PrologMQI.start()
andPrologThread.start()
if either is not already started.Answers are retrieved using
PrologThread.query_async_result()
. The query can be cancelled by callingPrologThread.cancel_query_async()
. The query is run on the same Prolog thread every time, emulating the Prolog top level.Args
value
- A Prolog query to execute as a string, just like you would run on the Prolog top level. e.g.
"member(X, [1, 2]), X = 2"
. find_all
True
(default) will run the query using Prolog'sfindall/3
to return all answers with one call toPrologThread.query_async_result()
.False
will return one answer perPrologThread.query_async_result()
call.query_timeout_seconds
None
uses thequery_timeout_seconds
set in theprolog_mqi
object passed toPrologThread
.
Raises
PrologError
if an exception occurs in Prolog when parsing the goal.PrologConnectionFailedError
if the query thread has unexpectedly exited. The MQI will no longer be listening after this exception.Any exception that happens when running the query is raised when calling
PrologThread.query_async_result()
Returns
True
def query_async_result(self, wait_timeout_seconds: float = None)
-
Get results from a query that was run using
PrologThread.query_async()
.Used to get results for all cases: if the query terminates normally, is cancelled by
PrologThread.cancel_query_async()
, or times out. Each call toquery_async_result()
returns one result and eitherNone
or raises an exception when there are no more results. Any raised exception except forPrologResultNotAvailableError
indicates there are no more results. IfPrologThread.query_async()
was run withfind_all == False
, multiplequery_async_result()
calls may be required before receiving the final None or raised exception.Examples
- If the query succeeds with N answers:
query_async_result()
calls 1 to N will receive each answer, in order, andquery_async_result()
call N+1 will returnNone
. - If the query fails (i.e. has no answers):
query_async_result()
call 1 will return False andquery_async_result()
call 2 will return
None`. - If the query times out after one answer,
query_async_result()
call 1 will return the first answer andquery_async_result()
call 2 will raisePrologQueryTimeoutError
. - If the query is cancelled after it had a chance to get 3 answers:
query_async_result()
calls 1 to 3 will receive each answer, in order, andquery_async_result()
call 4 will raisePrologQueryCancelledError
. - If the query throws an exception before returning any results,
query_async_result()
call 1 will raisePrologError
.
Note that, after calling
PrologThread.cancel_query_async()
, callingquery_async_result()
will return the "natural" result of the goal's execution. See documentation forPrologThread.cancel_query_async()
for more information.Args
wait_timeout_seconds
- Wait
wait_timeout_seconds
seconds for a result, or forever ifNone
. If the wait timeout is exceeded before a result is available, raisesPrologResultNotAvailableError
.
Raises
PrologNoQueryError
if there is no query in progress.PrologResultNotAvailableError
if there is a running query and no results were available inwait_timeout_seconds
.PrologQueryCancelledError
if the next answer was the exception caused byPrologThread.cancel_query_async()
. Indicates no more answers.PrologQueryTimeoutError
if the query timed out generating the next answer (possibly in a race condition before getting cancelled). Indicates no more answers.PrologError
if the next answer is an arbitrary exception thrown when the query was generating the next answer. This can happen afterPrologThread.cancel_query_async()
is called if the exception for cancelling the query is caught or the code hits another exception first. Indicates no more answers.PrologConnectionFailedError
if the query thread unexpectedly exited. The MQI will no longer be listening after this exception.Returns
False
- The query failed.
True
- The next answer is success with no free variables.
list
-
The query succeeded once with free variables or more than once with no free variables. There will be an item in the list for every answer. Each item will be:
True
if there were no free variables- A
dict
if there were free variables. Each key will be the name of a variable, each value will be the JSON representing the term it was unified with. Note that a special variable called$residuals
will be added to each answer that has residual variable constraints on it. This will contain a list of all the constraints on all the variables for that answer.
- If the query succeeds with N answers:
def start(self)
-
Connect to the
prolog_mqi
specified inPrologThread
and start a new thread in it. Launch SWI Prolog and start the Machine Query Interface using themqi/1
predicate iflaunch_mqi
isTrue
on that object. Does not start a Python thread.Does nothing if the thread is already started.
Raises
PrologLaunchError
iflaunch_mqi
isFalse
and the password does not match the server.Various socket errors if the server is not running or responding.
def stop(self)
-
Do an orderly stop of the thread running in the Prolog process associated with this object and close the connection to the
prolog_mqi
specified inPrologThread
.If an asynchronous query is running on that thread, it is halted using Prolog's
abort
.