The janus
API for Prolog Calling
Python
Contents
- Preliminary Definitions
- The Main Predicates To Call Python
- Python Reflection Predicates
- Other
janus
Predicates
This chapter describes each predicate in the
janus
Prolog calling Python API. Examples are
provided for some of the predicates, many more examples
can be found in Janus Examples for
Prolog Calling Python .
Preliminary Definitions
We introduce some terminology and calling conventions through a short series of examples.
janus
GoalTerms
Syntactically, a janus
GoalTerm
is either an atom or a compound term. If it is a compound
term, any argument to a goal whose outer functor is
=/2
is treated as a Python keyword argument.
As in Python any keyword argument must occur after all
positional arguments.
If GoalTerm is an atom it corresponds to a attribute call; if it is a compound term it corresponds to function or method call.
janus
GoalTerm Example
py_func(jns_rdflib,rdflib_write_file(Triples,'out.ttl',format=turtle),Return).
calls the Python function
jns_rdflib.rdflib_write_file()
to write
Triples
, a list of triples in Prolog format,
to the file new_sample.ttl
using the RDF
turtle (ttl
) format. For the Python function
jns_rdf.rdflib_write_file()
,
format
is a keyword, and turtle
is an allowed value for that keyword.
Keyword arguments are allowed in
py_call/[2,3,4]
, py_func/[3,4]
,
py_dot/[3,4]
, and
py_iter/[2,3,4]
.
janus
GoalSequences
janus
GoalSequence: Introductory Example
Consider a file dog.py
such as the
following (modified from the Python
documentation)
-------------- -------------- --------------
current_dog = ''
latest_trick = []
class Dog:
tricks = []
def __init__(self, name):
global current_dog
self.name = name
current_dog = name
def add_trick(self, trick):
global latest_trick
self.tricks.append(trick)
latest_trick = trick
return self.tricks
-------------- -------------- --------------
We can interact with this class as follows. Executing the either:
?- py_call(dog:'Dog'('Fido'), MyDog).
or
?- py_func(dog,'Dog'('Fido'), MyDog).
unifies Dog
with a reference to the Python
object created by initializing an instance of the
Dog
class via: dog.Dog('Fido')
.
Next, suppose we extend both goals.
?- py_call(dog:'Dog'('Fido'), MyDog),
py_call(MyDog:add_trick(roll_over),Tricks).
and
?- py_func(dog,'Dog'('Fido'), MyDog),
py_dot(MyDog,add_trick(roll_over),Tricks).
both goals not only create the dog MyDog
but add a trick to the repetoire of MyDog
and
return the current tricks of MyDog
. The
second goal shows in each case illustrates how to make a
method call to a Python object reference.
Suppose that there were no need to retain the dog
object in Prolog, but simply to update the global
variables of the module dog.py
The above
conjunctive goal could be rewritten as:
?- py_call(dog:'Dog'('Fido'):add_trick(roll_over), Tricks).
Finally, consider checking one of the attributes of the
module dog
. In this case the goals
?- py_call(dog:current_dog,Name)
and
?- py_dot(dog,current_dog,Name)
both unifiy Name
with the
dog.current_dog
and illustrate how to obtain
an attribute of a Python object reference.
janus
GoalSequence: Definition
Formally, a janus
GoalSequence is
defined inductively as:
Target:GoalTerm where
Target
is either an atom corresponding to a Python Module name, or a Python object reference; andGoalTerm
is defined as in 1.1.GoalSequence:GoalTerm where TargetSequence is a GoalSequence such that
py_call(GoalSequence,Return)
unifies
Return
with an atom corresponding to a module name or a Python object reference.
The Pseudo-Module
builtins
In addition to using Python module names, Python
built-in functions can be called using the “pseudo-module”
builtins
. For instance
py_func(builtins, float(’+1E6’),F).
produces the expected result:
F = 1000000.0
The pseudo-module builtins
is allowed in
the first argument of py_call/[2,3,4]
,
py_func/[3,4]
, py_dot/[3,4]
, and
epy_iter/[2,3,4]
.
Python Errors
A python_error
is a term of the form:
error(python_error(ErrorType,ErrorValue),OtherInformation)
where - ErrorType
is the Python type name
for the exception
type - ErrorValue
is a translation into
Prolog of the Python Object associated with the error
The Main Predicates to Call Python
Predicates py_func/3 py_func/4
- py_func(+Module, +Function,
?Return)
- py_func(+Module, +Function, ?Return,
+Options)
Ensures that the Python module Module
is
loaded, and calls Module.Function
unifying
the return of Function
with
Return
.
Function
is a Goal Term (1.1), while
Module
is an atom representing a Python
module. If Module
has not already been
loaded, it will be automatically loaded during the call.
Python modules are searched for in the paths maintained in
Python’s sys.path
list; these Python paths
can be queried from Prolog via py_lib_dir/1
and modified via py_add_lib_dir/1
.
Prolog Options
py_object(true)
This option returns most
Python data structures as object references, so that
attributes of those data structures can be queried if
needed. The only data not returned as object
references are
Objects of
boolean
typeObjects of
none
typeObjects of exactly the class
long
,float
orstring
. Objects that are proper subclasses of these types are returned as object references.
Error Cases
py_func/4
is called withModule
,Function
orOptions
uninstantiatedinstantiation_error
Module
is not a Prolog atom:type_error
Module
cannot be found in the current Python search paths or is otherwise unloadable:python_error
Function
is not a callable termtype_error
Function
does not correspond to a Python function inModule
or is otherwise uncallablepython_error
Options
contains an improper element or combination of elementsdomain_error
Function
contains a Prolog term that cannot be translated into Python:domain_error
Any other error thrown by Python during the call should
be caught and thrown as a Prolog
python_error
Predicates py_dot/3, py_dot/4
- py_dot(+Target, +MethAttr,
?Return)
- py_dot(+Target, +MethAttr, ?Return,
+Options)
Applies a method to Target
or obtains an
attribute value for Target
. More
specifically, Target
is a either Python
object reference or an atom representing a Python module.
MethAttr
is a Goal Term (1.1) that can either
be a Prolog compound term or a Prolog atom:
If
MethAttr
is a Prolog compound term corresponding to a Python method forTarget
, the method is called and its return unified withReturn
.If
MethAttr
is a Prolog atom corresponding to the name of an attribute ofTarget
, the attribute value (forTarget
) is accessed and unified withReturn
.
Options
is as described for
py_func/[3,4]
(2.1)
Error Cases
py_dot/4
is called withTarget
,MethAttr
orOptions
uninstantiated:instantiation_error
Target
is not a Prolog atom or Python object referencetype_error
MethAttr
is not a callable term or atom.type_error
MethAttr
does not correspond to a Python method or attribute forTarget
python_error
Options
contains an improper element, or combination of elements.domain_error
MethAttr
contains a Prolog term that cannot be translated into Python:domain_error
Any other error thrown by Python during the call should
be caught and thrown as a Prolog
python_error
.
Predicate py_setattr/3
- py_setattr(+Target, +Attr,
+Value)
If Target
is a module or an object, this
command is equivalent to the Python
setattr(Target, Attr, Value)
or in Prolog
py_setattr(Target, Name, Value) :-
py_call(Target, Obj, [py_object(true)]),
py_call(setattr(Obj, Name, Value)).
Error Cases
py_setattr/3
is called withTarget
,Attr
orValue
uninstantiated:instantiation_error
Target
is not a Python object reference or Prolog atomtype_error
Name
is not an atom.type_error
Value
contains a Prolog term that cannot be translated into Python:domain_error
Any other error thrown by Python during the call should
be caught and thrown as a Prolog
python_error
.
Predicates py_iter/2, py_iter/3
- py_iter(+Iterator, ?Value)
- py_iter(+Iterator,
?Value,+Options)
In py_iter/[2,3]
Iterator
can
either be a reference to a Python iterator object or a
janus
GoalSequene (1.2) that
produces a reference to a Python iterator object. In
either case, the first value of the iterator will be
returned by the goal and subsequence values returned via
backtracking (cf. Example <a href=“#jns-examp:lazy-ret”
data-reference-type=“ref” py_iter/[2,3]
when
there are no values left for Iterator
.
Options
are as described in
py_func/[3,4]
Examples
The example below uses the built-in iterator
range()
:
?- py_iter(range(1,3), X). X = 1 ; X = 2.
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
Error Cases
py_iter/[2,3]
is called withIterator
nonground orOptions
uninstantiatedinstantiation_error
- The GoalSequence
Iterator
contains a goalTarget:GoalTerm
whereTarget
is neither a Prolog atom nor a reference to a Python iterator object:type_error
Target
is an atom representing a Python module that cannot be found in the current Python search paths or is otherwise unloadable:python_error
GoalTerm
is neither a callable term nor an atomtype_error
Function
does not correspond to a Python function inModule
or is otherwise uncallablepython_error
GoalTerm
contains a Prolog term that cannot be translated into Python:domain_error
Options
contains an improper element, or combination of elementsdomain_error
Predicates py_call/1, py_call/2, py_call/3
- py_call(+GoalSequence)
- py_call(+GoalSequence,
?Return)
- py_call(+GoalSequence, ?Return,
+Options)
Execute GoalSequence
in Python and and
unify the result with Return
(in
py_call/[2,3]
). GoalSequence
is
a janus
GoalSequence as described in
1.2. Options
is as described for
py_func/[3,4]
Error Cases
py_call/[2,3,4]
is called with a nongroundGoalSequene
orOptions
uninstantiatedinstantiation_error
GoalSequence
contains a goalTarget:GoalTerm
whereTarget
is neither a Prolog atom nor a reference to a Python object:type_error
Target
is an atom representing a Python module that cannot be found in the current Python search paths or is otherwise unloadable:python_error
GoalTerm
is neither a callable term nor an atomtype_error
Function
does not correspond to a Python function inModule
or is otherwise uncallablepython_error
GoalTerm
contains a Prolog term that cannot be translated into Python:domain_error
Options
contains an improper element, or combination of elementsdomain_error
Python Reflection Predicates
Predicate py_type/2
py_type(+ObjRef, ?Type)
True when theType
unifies with the name of the type ofObjRef
represented as a Prolog atom. This is the same astype(ObjRef).__name__
in Python.
Error Cases
-
py_type/2
is called with a nonground
ObjRef
- instantiation_error
-
py_type/2is called with
Typeneither a variable nor an atom -
type_error`
Predicate py_isinstance/2
- py_isinstance(+ObjRef, +Type)
True ifObjRef
is an instance ofType
or an instance of one of the sub types ofType
. This is the same asisinstance(ObjRef)
in Python.Type
must be instantiated either to a termModule:Type
or to an atom to refer to a built-in type.
Error Cases
- ObjRef
or Type
is uninstantiaed at call -
instantiation_error - ObjRef
is not a Python
object reference, or Type
is not an atom -
type_error
Predicate py_module_exists/1
py_module_exists(+Module)
True if the atomModule
is a module loaded into Python or loadable into Python. The predicate calls the Pythonimportlib.util.find_spec(Module)
Error Cases
- Module
is uninstantiaed at call - instantiation_error - `Module
is not an atom - type_error
Other janus
predicates
Predicate py_is_object/1
- py_is_object(+ObjRef)
Succeeds ifObjRef
is a Python object reference and fails otherwise. Different Prologs that implement Janus will have different representations of Python object references, so this predicate should be used to determine whether a term is a Python object reference.
Predicate py_free/1
- py_free(+ObjRef)
In general when janus
bi-translates
between Python objects and Prolog terms it performs a
copy: this has the advantage that each system can perform
its own memory management independently of the other. The
exception is when a reference to a Python object is passed
to Prolog. In this case, Python must explicitly be told
that the Python object can be reclaimed, and this is done
through py_free/1
.
Calling py_free(ObjRef)
causes Python to
decrement the reference count of ObjRef
. This
may not actually delete the object because the object may
have references inside Python.
On some implementations, reclaiming references from
Prolog to Python objects is done by the Prolog garbage
collector. For portability, py_free/1
must be
called exactly once on each object reference
obtained from Python. Further usage of the object
reference may cause undefined behavior.
Predicates py_pp/1, py_pp/2, py_pp/3
- py_pp(+Stream,
+Term,+Options)
- py_pp(+Stream, +Term)
- py_pp(+Term)
Pretty prints a janus
Python term
Term
to . The implementation and behavior of
these predicates are system-dependent.
Common Error Cases
Stream
.Term
orOptions
is not instantiated- instantiation_error
- Stream_or_alias is neither a variable, a stream term,
nor an alias; or
Options
contains an improper elementdomain_error
Predicates
py_add_lib_dir/1, py_add_lib_dir/2
py_add_lib_dir(+Dir)
py_add_lib_dir(+Dir, +Where)
Add a directory to the Python module search path (
sys.path
). In the second form,Where
is one offirst
orlast
.py_add_lib_dir/1
adds the directory aslast
. The propertysys:path
is not modified if it already containsDir
.
Error Cases
Dir
orWhere
is uninstantiated at call- instantiation error
Dir
orWhere
are not instantiated to an atom- type error
Where
is notfirst
orlast
- domain_error
Predicate py_lib_dirs/1
- py_lib_dirs(?Path)
This convenience and compatibility predicate unifies
Path
with a list of the current Python
library directories as obtained from
sys.path
. There are no associated error
cases.
Predicate values/3
values(+Dict, +Path, ?Val)
Convenience and compatibility predicate to obtain a value from a (possibly nested)janus
dictionary term . The goalvalues(D,key1,V)
is equivalent to the Python expression
D[key1]
while
values(D,[key1,key2,key3],V)
is equivalent to the Python expression
D[key1][key2][key3]
There are no error conditions associated with this predicate.
Predicates items/2, key/2, keys/2
These convenience predicates provide a Prolog equivalent of some of Python’s dictionary access routines.
keys(+Dict, ?Keys)
True when
Keys
is a list of keys that appear inDict
.key(+Dict, ?Key)
True when
Key
is a key inDict
. Backtracking enumerates all known keys.items(+Dict, ?Items)
True when
Items
is a list ofKey:Value
mappings that appear inDict
.