Did you know ... | Search Documentation: |
janus.pl -- Call Python from Prolog |
This library implements calling Python from Prolog. It is available
directly from Prolog if the janus package is bundled. The library
provides access to an embedded Python instance. If SWI-Prolog is
embedded into Python using the Python package janus-swi
, this
library is provided either from Prolog or from the Python package.
Normally, the Prolog user can simply start calling Python using py_call/2 or friends. In special cases it may be needed to initialize Python with options using py_initialize/3 and optionally the Python search path may be extended using py_add_lib_dir/1.
sys.version
. If a Python virtual environment (venv) is
active, indicate this with the location of this environment found.
Arguments to Python functions use the Python conventions. Both
positional and keyword arguments are supported. Keyword
arguments are written as Name = Value
and must appear after the
positional arguments.
Below are some examples.
% call a built-in ?- py_call(print("Hello World!\n")). true. % call a built-in (alternative) ?- py_call(builtins:print("Hello World!\n")). true. % call function in a module ?- py_call(sys:getsizeof([1,2,3]), Size). Size = 80. % call function on an attribute of a module ?- py_call(sys:path:append("/home/bob/janus")). true % get attribute from a module ?- py_call(sys:path, Path) Path = ["dir1", "dir2", ...]
Given a class in a file dog.py
such as the following example from
the Python documentation
class Dog: tricks = [] def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick)
We can interact with this class as below. Note that $Doc
in the
SWI-Prolog toplevel refers to the last toplevel binding for the
variable Dog.
?- py_call(dog:'Dog'("Fido"), Dog). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:add_trick("roll_over")). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:tricks, Tricks). Dog = <py_Dog>(0x7f095c9d02e0), Tricks = ["roll_over"]
If the principal term of the first argument is not Target:Func
,
The argument is evaluated as the initial target, i.e., it must be an
object reference or a module. For example:
?- py_call(dog:'Dog'("Fido"), Dog), py_call(Dog, X). Dog = X, X = <py_Dog>(0x7fa8cbd12050). ?- py_call(sys, S). S = <py_module>(0x7fa8cd582390).
Options processed:
true
(default false
), translate the return as a Python
object reference. Some objects are always translated to
Prolog, regardless of this flag. These are the Python constants
None
, True
and False
as well as instances of the
Python base classes int
, float
, str
or tuple
. Instances
of sub classes of these base classes are controlled by this
option.atom
(default), translate a Python String into a
Prolog atom. If Type is string
, translate into a Prolog string.
Strings are more efficient if they are short lived.dict
(default) to map a Python dict to a SWI-Prolog
dict if all keys can be represented. If {}
or not all keys
can be represented, Return is unified to a term {k:v, ...}
or py({})
if the Python dict is empty.Obj:Attr = Value
construct is not accepted.__iter__
on the result to get the iterator itself.__next__
function of the iterator.
The example below uses the built-in iterator range()
:
?- py_iter(range(1,3), X). X = 1 ; X = 2.
Note that the implementation performs a look ahead, i.e., after successful unification it calls `next()` again. On failure the Prolog predicate succeeds deterministically. On success, the next candidate is stored.
Note that a Python generator is a Python iterator. Therefore,
given the Python generator expression below, we can use
py_iter(squares(1,5),X)
to generate the squares on backtracking.
def squares(start, stop): for i in range(start, stop): yield i * i
py_setattr(Target, Name, Value) :- py_call(Target, Obj, [py_object(true)]), py_call(setattr(Obj, Name, Value)).
existence_error
. Note that by decrementing the
reference count, we make the reference invalid from Prolog. This may
not actually delete the object because the object may have
references inside Python.
Prolog references to Python objects are subject to atom garbage collection and thus normally do not need to be freed explicitly.
once(Goal)
while holding the Phyton GIL (Global
Interpreter Lock). Note that all predicates that interact with
Python lock the GIL. This predicate is only required if we wish to
make multiple calls to Python while keeping the GIL. The GIL is a
recursive lock and thus calling py_call/1,2 while holding the GIL
does not deadlock.Note that this predicate returns the Prolog threads that locked the GIL. It is however possible that Python releases the GIL, for example if it performs a blocking call. In this scenario, some other thread or no thread may hold the gil.
py_call(Module:Function, Return)
. See py_call/2 for
details.
py_call(ObjRef:MethAttr,
Return)
. See py_call/2 for details.
janus
as below.
from janus import *
So, we can do
?- py_shell. ... >>> query_once("writeln(X)", {"X":"Hello world"}) Hello world {'truth': True}
If possible, we enable command line editing using the GNU readline library.
When used in an environment where Prolog does not use the file
handles 0,1,2 for the standard streams, e.g., in swipl-win
,
Python's I/O is rebound to use Prolog's I/O. This includes Prolog's
command line editor, resulting in a mixed history of Prolog and
Pythin commands.
pformat()
from the Python module
pprint
to do the actual formatting. Options is translated into
keyword arguments passed to pprint.pformat()
. In addition, the
option nl(Bool)
is processed. When true
(default), we use
pprint.pp()
, which makes the output followed by a newline. For
example:
?- py_pp(py{a:1, l:[1,2,3], size:1000000}, [underscore_numbers(true)]). {'a': 1, 'l': [1, 2, 3], 'size': 1_000_000}
type(ObjRef).__name__
in Python.
isinstance(ObjRef)
in
Python.
hasattr()
. If Name is unbound, this enumerates
the members of py_object_dir/2.
__import__()
built-in and added to a table that maps Prolog atoms to imported
modules. This predicate explicitly imports a module and allows it to
be associated with a different name. This is useful for loading
nested modules, i.e., a specific module from a Python package as
well as for avoiding conflicts. For example, with the Python
selenium
package installed, we can do in Python:
>>> from selenium import webdriver >>> browser = webdriver.Chrome()
Without this predicate, we can do
?- py_call('selenium.webdriver':'Chrome'(), Chrome).
For a single call this is fine, but for making multiple calls it gets cumbersome. With this predicate we can write this.
?- py_import('selenium.webdriver', []). ?- py_call(webdriver:'Chrome'(), Chrome).
By default, the imported module is associated to an atom created from the last segment of the dotted name. Below we use an explicit name.
?- py_import('selenium.webdriver', [as(browser)]). ?- py_call(browser:'Chrome'(), Chrome).
string
quasi quotation that supports long
strings in SWI-Prolog. For example:
:- use_module(library(strings)). :- py_module(hello, {|string|| | def say_hello_to(s): | print(f"hello {s}") |}).
Calling this predicate multiple times with the same Module and Source is a no-op. Called with a different source creates a new Python module that replaces the old in the global namespace.
py_argv
and an empty
Options list.
Calling this predicate while the Python is already initialized is a no-op. This predicate is thread-safe, where the first call initializes Python.
In addition to initializing the Python system, it
janus.py
to the Python module
search path.first
or last
. py_add_lib_dir/1 adds the
directory as last
. The property sys:path
is not modified if it
already contains Dir.
Dir is in Prolog notation. The added directory is converted to an absolute path using the OS notation using prolog_to_os_filename/2.
If Dir is a relative path, it is taken relative to Prolog source file when used as a directive and relative to the process working directory when called as a predicate.
The following predicates are exported from this file while their implementation is defined in imported modules or non-module files loaded by this module.
Arguments to Python functions use the Python conventions. Both
positional and keyword arguments are supported. Keyword
arguments are written as Name = Value
and must appear after the
positional arguments.
Below are some examples.
% call a built-in ?- py_call(print("Hello World!\n")). true. % call a built-in (alternative) ?- py_call(builtins:print("Hello World!\n")). true. % call function in a module ?- py_call(sys:getsizeof([1,2,3]), Size). Size = 80. % call function on an attribute of a module ?- py_call(sys:path:append("/home/bob/janus")). true % get attribute from a module ?- py_call(sys:path, Path) Path = ["dir1", "dir2", ...]
Given a class in a file dog.py
such as the following example from
the Python documentation
class Dog: tricks = [] def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick)
We can interact with this class as below. Note that $Doc
in the
SWI-Prolog toplevel refers to the last toplevel binding for the
variable Dog.
?- py_call(dog:'Dog'("Fido"), Dog). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:add_trick("roll_over")). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:tricks, Tricks). Dog = <py_Dog>(0x7f095c9d02e0), Tricks = ["roll_over"]
If the principal term of the first argument is not Target:Func
,
The argument is evaluated as the initial target, i.e., it must be an
object reference or a module. For example:
?- py_call(dog:'Dog'("Fido"), Dog), py_call(Dog, X). Dog = X, X = <py_Dog>(0x7fa8cbd12050). ?- py_call(sys, S). S = <py_module>(0x7fa8cd582390).
Options processed:
true
(default false
), translate the return as a Python
object reference. Some objects are always translated to
Prolog, regardless of this flag. These are the Python constants
None
, True
and False
as well as instances of the
Python base classes int
, float
, str
or tuple
. Instances
of sub classes of these base classes are controlled by this
option.atom
(default), translate a Python String into a
Prolog atom. If Type is string
, translate into a Prolog string.
Strings are more efficient if they are short lived.dict
(default) to map a Python dict to a SWI-Prolog
dict if all keys can be represented. If {}
or not all keys
can be represented, Return is unified to a term {k:v, ...}
or py({})
if the Python dict is empty.
Arguments to Python functions use the Python conventions. Both
positional and keyword arguments are supported. Keyword
arguments are written as Name = Value
and must appear after the
positional arguments.
Below are some examples.
% call a built-in ?- py_call(print("Hello World!\n")). true. % call a built-in (alternative) ?- py_call(builtins:print("Hello World!\n")). true. % call function in a module ?- py_call(sys:getsizeof([1,2,3]), Size). Size = 80. % call function on an attribute of a module ?- py_call(sys:path:append("/home/bob/janus")). true % get attribute from a module ?- py_call(sys:path, Path) Path = ["dir1", "dir2", ...]
Given a class in a file dog.py
such as the following example from
the Python documentation
class Dog: tricks = [] def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick)
We can interact with this class as below. Note that $Doc
in the
SWI-Prolog toplevel refers to the last toplevel binding for the
variable Dog.
?- py_call(dog:'Dog'("Fido"), Dog). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:add_trick("roll_over")). Dog = <py_Dog>(0x7f095c9d02e0). ?- py_call($Dog:tricks, Tricks). Dog = <py_Dog>(0x7f095c9d02e0), Tricks = ["roll_over"]
If the principal term of the first argument is not Target:Func
,
The argument is evaluated as the initial target, i.e., it must be an
object reference or a module. For example:
?- py_call(dog:'Dog'("Fido"), Dog), py_call(Dog, X). Dog = X, X = <py_Dog>(0x7fa8cbd12050). ?- py_call(sys, S). S = <py_module>(0x7fa8cd582390).
Options processed:
true
(default false
), translate the return as a Python
object reference. Some objects are always translated to
Prolog, regardless of this flag. These are the Python constants
None
, True
and False
as well as instances of the
Python base classes int
, float
, str
or tuple
. Instances
of sub classes of these base classes are controlled by this
option.atom
(default), translate a Python String into a
Prolog atom. If Type is string
, translate into a Prolog string.
Strings are more efficient if they are short lived.dict
(default) to map a Python dict to a SWI-Prolog
dict if all keys can be represented. If {}
or not all keys
can be represented, Return is unified to a term {k:v, ...}
or py({})
if the Python dict is empty.Obj:Attr = Value
construct is not accepted.__iter__
on the result to get the iterator itself.__next__
function of the iterator.
The example below uses the built-in iterator range()
:
?- py_iter(range(1,3), X). X = 1 ; X = 2.
Note that the implementation performs a look ahead, i.e., after successful unification it calls `next()` again. On failure the Prolog predicate succeeds deterministically. On success, the next candidate is stored.
Note that a Python generator is a Python iterator. Therefore,
given the Python generator expression below, we can use
py_iter(squares(1,5),X)
to generate the squares on backtracking.
def squares(start, stop): for i in range(start, stop): yield i * i
py_call(Module:Function, Return)
. See py_call/2 for
details.
py_call(ObjRef:MethAttr,
Return)
. See py_call/2 for details.
pformat()
from the Python module
pprint
to do the actual formatting. Options is translated into
keyword arguments passed to pprint.pformat()
. In addition, the
option nl(Bool)
is processed. When true
(default), we use
pprint.pp()
, which makes the output followed by a newline. For
example:
?- py_pp(py{a:1, l:[1,2,3], size:1000000}, [underscore_numbers(true)]). {'a': 1, 'l': [1, 2, 3], 'size': 1_000_000}
pformat()
from the Python module
pprint
to do the actual formatting. Options is translated into
keyword arguments passed to pprint.pformat()
. In addition, the
option nl(Bool)
is processed. When true
(default), we use
pprint.pp()
, which makes the output followed by a newline. For
example:
?- py_pp(py{a:1, l:[1,2,3], size:1000000}, [underscore_numbers(true)]). {'a': 1, 'l': [1, 2, 3], 'size': 1_000_000}
first
or last
. py_add_lib_dir/1 adds the
directory as last
. The property sys:path
is not modified if it
already contains Dir.
Dir is in Prolog notation. The added directory is converted to an absolute path using the OS notation using prolog_to_os_filename/2.
If Dir is a relative path, it is taken relative to Prolog source file when used as a directive and relative to the process working directory when called as a predicate.