View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2023-2024, SWI-Prolog Solutions b.v.
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(janus,
   36          [ py_version/0,
   37
   38            py_call/1,                  % +Call
   39            py_call/2,                  % +Call, -Return
   40            py_call/3,                  % +Call, -Return, +Options
   41	    py_iter/2,			% +Call, -Return
   42	    py_iter/3,			% +Call, -Return, +Options
   43            py_setattr/3,               % +On, +Name, +Value
   44            py_free/1,			% +Obj
   45	    py_is_object/1,		% @Term
   46	    py_is_dict/1,		% @Term
   47	    py_with_gil/1,		% :Goal
   48	    py_gil_owner/1,		% -ThreadID
   49
   50            py_func/3,                  % +Module, +Func, -Return
   51            py_func/4,                  % +Module, +Func, -Return, +Options
   52            py_dot/3,                   % +ObjRef, +Meth, ?Ret
   53            py_dot/4,                   % +ObjRef, +Meth, -Ret, +Options
   54
   55            values/3,                   % +Dict, +Path, ?Val
   56            keys/2,                     % +Dict, ?Keys
   57            key/2,                      % +Dict, ?Key
   58            items/2,                    % +Dict, ?Items
   59
   60            py_shell/0,
   61
   62	    py_pp/1,                    % +Term
   63            py_pp/2,                    % +Stream, +Term
   64            py_pp/3,                    % +Stream, +Term, +Options
   65
   66            py_object_dir/2,            % +ObjRef, -List
   67            py_object_dict/2,           % +ObjRef, -Dict
   68            py_obj_dir/2,               % +ObjRef, -List (deprecated)
   69            py_obj_dict/2,              % +ObjRef, -Dict (deprecated)
   70            py_type/2,			% +ObjRef, -Type:atom
   71            py_isinstance/2,            % +ObjRef, +Type
   72            py_module_exists/1,         % +Module
   73            py_hasattr/2,               % +Module, ?Symbol
   74
   75            py_import/2,                % +Spec, +Options
   76            py_module/2,                % +Module:atom, +Source:string
   77
   78            py_initialize/3,            % +Program, +Argv, +Options
   79            py_lib_dirs/1,              % -Dirs
   80            py_add_lib_dir/1,           % +Dir
   81            py_add_lib_dir/2,           % +Dir,+Where
   82
   83            op(200, fy, @),             % @constant
   84            op(50,  fx, #)              % #Value
   85          ]).   86:- meta_predicate py_with_gil(0).   87
   88:- use_module(library(apply_macros), []).   89:- autoload(library(lists), [append/3, member/2, append/2, last/2]).   90:- autoload(library(apply),
   91            [maplist/2, exclude/3, maplist/3, convlist/3, partition/4]).   92:- autoload(library(error), [must_be/2, domain_error/2]).   93:- autoload(library(dicts), [dict_keys/2]).   94:- autoload(library(option), [dict_options/2, select_option/4, option/2]).   95:- autoload(library(prolog_code), [comma_list/2]).   96:- autoload(library(readutil), [read_line_to_string/2, read_file_to_string/3]).   97:- autoload(library(wfs), [call_delays/2, delays_residual_program/2]).   98:- autoload(library(dcg/high_order), [sequence//2, sequence//3]).   99
  100:- if(\+current_predicate(py_call/1)).  101:- if(current_prolog_flag(windows, true)).  102:- use_module(library(shlib), [win_add_dll_directory/1]).  103
  104% Just having the Python dir in PATH seems insufficient. We also need to
  105% add the directory to the DLL search path.
  106add_python_dll_dir :-
  107    (   current_prolog_flag(msys2, true)
  108    ->  absolute_file_name(path('libpython3.dll'), DLL, [access(read)])
  109    ;   absolute_file_name(path('python3.dll'), DLL, [access(read)])
  110    ),
  111    file_directory_name(DLL, Dir),
  112    win_add_dll_directory(Dir).
  113:- initialization(add_python_dll_dir, now).  114:- endif.  115
  116:- use_foreign_library(foreign(janus), [visibility(global)]).  117:- endif.  118
  119:- predicate_options(py_call/3, 3,
  120                     [ py_object(boolean),
  121                       py_string_as(oneof([string,atom]))
  122                     ]).  123:- predicate_options(py_func/4, 4,
  124                     [ pass_to(py_call/3, 3)
  125                     ]).  126:- predicate_options(py_dot/5, 5,
  127                     [ pass_to(py_call/3, 3)
  128                     ]).  129
  130:- public
  131    py_initialize/0,
  132    py_call_string/3,
  133    py_write/2,
  134    py_readline/4.  135
  136:- create_prolog_flag(py_backtrace,       true, [type(boolean), keep(true)]).  137:- create_prolog_flag(py_backtrace_depth, 4,    [type(integer), keep(true)]).  138:- create_prolog_flag(py_argv,		  [],   [type(term), keep(true)]).

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.

 py_version is det
Print version info on the embedded Python installation based on Python sys.version. If a Python virtual environment (venv) is active, indicate this with the location of this environment found.
  160py_version :-
  161    py_call(sys:version, PythonVersion),
  162    py_call(janus_swi:version_str(), JanusVersion),
  163    print_message(information, janus(version(JanusVersion, PythonVersion))),
  164    (   py_venv(VEnvDir, EnvSiteDir)
  165    ->  print_message(information, janus(venv(VEnvDir, EnvSiteDir)))
  166    ;   true
  167    ).
 py_call(+Call) is det
 py_call(+Call, -Return) is det
 py_call(+Call, -Return, +Options) is det
Call Python and return the result of the called function. Call has the shape `[Target][:Action]*`, where Target is either a Python module name or a Python object reference. Each Action is either an atom to get the denoted attribute from current Target or it is a compound term where the first argument is the function or method name and the arguments provide the parameters to the Python function. On success, the returned Python object is translated to Prolog. Action without a Target denotes a buit-in function.

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", ...]

% reference a Python builtin (class or function)
?- py_call(builtins:list, L, [py_object(true)]).
L = <py>(...,type).

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>(0x7f095c9d02e0,'Dog').

?- py_call($Dog:add_trick("roll_over")).
Dog = <py>(0x7f095c9d02e0,'Dog').

?- py_call($Dog:tricks, Tricks).
Dog = <py>(0x7f095c9d02e0,'Dog'),
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>(0x7fa8cbd12050,'Dog').
?- py_call(sys, S).
   S = <py>(0x7fa8cd582390,module).

Options processed:

py_object(Boolean)
If 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.
py_string_as(+Type)
If Type is 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.
py_dict_as(+Type)
One of 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.
Compatibility
- PIP. The options py_string_as and py_dict_as are SWI-Prolog specific, where SWI-Prolog Janus represents Python strings as atoms as required by the PIP and it represents Python dicts by default as SWI-Prolog dicts. The predicates values/3, keys/2, etc. provide portable access to the data in the dict.
 py_iter(+Iterator, -Value) is nondet
 py_iter(+Iterator, -Value, +Options) is nondet
True when Value is returned by the Python Iterator. Python iterators may be used to implement non-deterministic foreign predicates. The implementation uses these steps:
  1. Evaluate Iterator as py_call/2 evaluates its first argument, except the Obj:Attr = Value construct is not accepted.
  2. Call __iter__ on the result to get the iterator itself.
  3. Get the __next__ function of the iterator.
  4. Loop over the return values of the next function. If the Python return value unifies with Value, succeed with a choicepoint. Abort on Python or unification exceptions.
  5. Re-satisfaction continues at (4).

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
Arguments:
Options- is processed as with py_call/3.
Compatibility
- PIP. The same remarks as for py_call/2 apply.
bug
- Iterator may not depend on janus.query(), i.e., it is not possible to iterate over a Python iterator that under the hoods relies on a Prolog non-deterministic predicate.
 py_setattr(+Target, +Name, +Value) is det
Set a Python attribute on an object. If Target is an atom, it is interpreted as a module. Otherwise it is normally an object reference. py_setattr/3 allows for chaining and behaves as if defined as
py_setattr(Target, Name, Value) :-
    py_call(Target, Obj, [py_object(true)]),
    py_call(setattr(Obj, Name, Value)).
Compatibility
- PIP
 py_run(+String, +Globals, +Locals, -Result, +Options) is det
Interface to Py_CompileString() followed by PyEval_EvalCode(). Options:
file_name(String)
Errors are reported against this pseudo file name
start(Token)
One of eval, file (default) or single.
Arguments:
Globals- is a dict
Locals- is a dict
 py_is_object(@Term) is semidet
True when Term is a Python object reference. Fails silently if Term is any other Prolog term.
Errors
- existence_error(py_object, Term) is raised of Term is a Python object, but it has been freed using py_free/1.
Compatibility
- PIP. The SWI-Prolog implementation is safe in the sense that an arbitrary term cannot be confused with a Python object and a reliable error is generated if the references has been freed. Portable applications can not rely on this.
 py_is_dict(@Term) is semidet
True if Term is a Prolog term that represents a Python dict.
Compatibility
- PIP. The SWI-Prolog version accepts both a SWI-Prolog dict and the {k:v,...} representation. See py_dict_as option of py_call/2.
  368py_is_dict(Dict), is_dict(Dict) => true.
  369py_is_dict(py({})) => true.
  370py_is_dict(py({KV})) => is_kv(KV).
  371py_is_dict({KV}) => is_kv(KV).
  372
  373is_kv((K:V,T)) => ground(K), ground(V), is_kv(T).
  374is_kv(K:V) => ground(K), ground(V).
 py_free(+Obj) is det
Immediately free (decrement the reference count) for the Python object Obj. Further reference to Obj using e.g., py_call/2 or py_free/1 raises an 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.

Compatibility
- PIP. The SWI-Prolog implementation is safe and normally reclaiming Python object can be left to the garbage collector. Portable applications may not assume garbage collection of Python objects and must ensure to call py_free/1 exactly once on any Python object reference. Not calling py_free/1 leaks the Python object. Calling it twice may lead to undefined behavior.
 py_with_gil(:Goal) is semidet
Run Goal as 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.
 py_gil_owner(-Thread) is semidet
True when the Python GIL is owned by Thread. Note that, unless Thread is the calling thread, this merely samples the current state and may thus no longer be true when the predicate succeeds. This predicate is intended to help diagnose deadlock problems.

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.

  418		 /*******************************
  419		 *         COMPATIBILIY		*
  420		 *******************************/
 py_func(+Module, +Function, -Return) is det
 py_func(+Module, +Function, -Return, +Options) is det
Call Python Function in Module. The SWI-Prolog implementation is equivalent to py_call(Module:Function, Return). See py_call/2 for details.
Compatibility
- PIP. See py_call/2 for notes. Note that, as this implementation is based on py_call/2, Function can use chaining, e.g., py_func(sys, path:append(dir), Return) is accepted by this implementation, but not portable.
  434py_func(Module, Function, Return) :-
  435    py_call(Module:Function, Return).
  436py_func(Module, Function, Return, Options) :-
  437    py_call(Module:Function, Return, Options).
 py_dot(+ObjRef, +MethAttr, -Ret) is det
 py_dot(+ObjRef, +MethAttr, -Ret, +Options) is det
Call a method or access an attribute on the object ObjRef. The SWI-Prolog implementation is equivalent to py_call(ObjRef:MethAttr, Return). See py_call/2 for details.
Compatibility
- PIP. See py_func/3 for details.
  448py_dot(ObjRef, MethAttr, Ret) :-
  449    py_call(ObjRef:MethAttr, Ret).
  450py_dot(ObjRef, MethAttr, Ret, Options) :-
  451    py_call(ObjRef:MethAttr, Ret, Options).
  452
  453
  454		 /*******************************
  455		 *   PORTABLE ACCESS TO DICTS	*
  456		 *******************************/
 values(+Dict, +Path, ?Val) is semidet
Get the value associated with Dict at Path. Path is either a single key or a list of keys.
Compatibility
- PIP. Note that this predicate handle a SWI-Prolog dict, a {k:v, ...} term as well as py({k:v, ...}.
  466values(Dict, Key, Val), is_dict(Dict), atom(Key) =>
  467    get_dict(Key, Dict, Val).
  468values(Dict, Keys, Val), is_dict(Dict), is_list(Keys) =>
  469    get_dict_path(Keys, Dict, Val).
  470values(py({CommaDict}), Key, Val) =>
  471    comma_values(CommaDict, Key, Val).
  472values({CommaDict}, Key, Val) =>
  473    comma_values(CommaDict, Key, Val).
  474
  475get_dict_path([], Val, Val).
  476get_dict_path([H|T], Dict, Val) :-
  477    get_dict(H, Dict, Val0),
  478    get_dict_path(T, Val0, Val).
  479
  480comma_values(CommaDict, Key, Val), atom(Key) =>
  481    comma_value(Key, CommaDict, Val).
  482comma_values(CommaDict, Keys, Val), is_list(Keys) =>
  483    comma_value_path(Keys, CommaDict, Val).
  484
  485comma_value(Key, Key:Val0, Val) =>
  486    Val = Val0.
  487comma_value(Key, (_,Tail), Val) =>
  488    comma_value(Key, Tail, Val).
  489
  490comma_value_path([], Val, Val).
  491comma_value_path([H|T], Dict, Val) :-
  492    comma_value(H, Dict, Val0),
  493    comma_value_path(T, Val0, Val).
 keys(+Dict, ?Keys) is det
True when Keys is a list of keys that appear in Dict.
Compatibility
- PIP. Note that this predicate handle a SWI-Prolog dict, a {k:v, ...} term as well as py({k:v, ...}.
  502keys(Dict, Keys), is_dict(Dict) =>
  503    dict_keys(Dict, Keys).
  504keys(py({CommaDict}), Keys) =>
  505    comma_dict_keys(CommaDict, Keys).
  506keys({CommaDict}, Keys) =>
  507    comma_dict_keys(CommaDict, Keys).
  508
  509comma_dict_keys((Key:_,T), Keys) =>
  510    Keys = [Key|KT],
  511    comma_dict_keys(T, KT).
  512comma_dict_keys(Key:_, Keys) =>
  513    Keys = [Key].
 key(+Dict, ?Key) is nondet
True when Key is a key in Dict. Backtracking enumerates all known keys.
Compatibility
- PIP. Note that this predicate handle a SWI-Prolog dict, a {k:v, ...} term as well as py({k:v, ...}.
  523key(Dict, Key), is_dict(Dict) =>
  524    dict_pairs(Dict, _Tag, Pairs),
  525    member(Key-_, Pairs).
  526key(py({CommaDict}), Keys) =>
  527    comma_dict_key(CommaDict, Keys).
  528key({CommaDict}, Keys) =>
  529    comma_dict_key(CommaDict, Keys).
  530
  531comma_dict_key((Key:_,_), Key).
  532comma_dict_key((_,T), Key) :-
  533    comma_dict_key(T, Key).
 items(+Dict, ?Items) is det
True when Items is a list of Key:Value that appear in Dict.
Compatibility
- PIP. Note that this predicate handle a SWI-Prolog dict, a {k:v, ...} term as well as py({k:v, ...}.
  542items(Dict, Items), is_dict(Dict) =>
  543    dict_pairs(Dict, _, Pairs),
  544    maplist(pair_item, Pairs, Items).
  545items(py({CommaDict}), Keys) =>
  546    comma_dict_items(CommaDict, Keys).
  547items({CommaDict}, Keys) =>
  548    comma_dict_items(CommaDict, Keys).
  549
  550pair_item(K-V, K:V).
  551
  552comma_dict_items((Key:Value,T), Keys) =>
  553    Keys = [Key:Value|KT],
  554    comma_dict_items(T, KT).
  555comma_dict_items(Key:Value, Keys) =>
  556    Keys = [Key:Value].
  557
  558
  559		 /*******************************
  560		 *             SHELL		*
  561		 *******************************/
 py_shell
Start an interactive Python REPL loop using the embedded Python interpreter. The interpreter first imports 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.

  587py_shell :-
  588    import_janus,
  589    py_call(janus_swi:interact(), _).
  590
  591import_janus :-
  592    py_call(sys:hexversion, V),
  593    V >= 0x030A0000,                    % >= 3.10
  594    !,
  595    py_run("from janus_swi import *", py{}, py{}, _, []).
  596import_janus :-
  597    print_message(warning, janus(py_shell(no_janus))).
  598
  599
  600		 /*******************************
  601		 *          UTILITIES           *
  602		 *******************************/
 py_pp(+Term) is det
 py_pp(+Term, +Options) is det
 py_pp(+Stream, +Term, +Options) is det
Pretty prints the Prolog translation of a Python data structure in Python syntax. This exploits 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}
Compatibility
- PIP
  624py_pp(Term) :-
  625    py_pp(current_output, Term, []).
  626
  627py_pp(Term, Options) :-
  628    py_pp(current_output, Term, Options).
  629
  630py_pp(Stream, Term, Options) :-
  631    select_option(nl(NL), Options, Options1, true),
  632    (   NL == true
  633    ->  Method = pp
  634    ;   Method = pformat
  635    ),
  636    opts_kws(Options1, Kws),
  637    PFormat =.. [Method, Term|Kws],
  638    py_call(pprint:PFormat, String),
  639    write(Stream, String).
  640
  641opts_kws(Options, Kws) :-
  642    dict_options(Dict, Options),
  643    dict_pairs(Dict, _, Pairs),
  644    maplist(pair_kws, Pairs, Kws).
  645
  646pair_kws(Name-Value, Name=Value).
 py_object_dir(+ObjRef, -List) is det
 py_object_dict(+ObjRef, -Dict) is det
Examine attributes of an object. The predicate py_object_dir/2 fetches the names of all attributes, while py_object_dir/2 gets a dict with all attributes and their values.
Compatibility
- PIP
  658py_object_dir(ObjRef, List) :-
  659    py_call(ObjRef:'__dir__'(), List).
  660
  661py_object_dict(ObjRef, Dict) :-
  662    py_call(ObjRef:'__dict__', Dict).
 py_obj_dir(+ObjRef, -List) is det
 py_obj_dict(+ObjRef, -Dict) is det
deprecated
- Use py_object_dir/2 or py_object_dict/2.
  669py_obj_dir(ObjRef, List) :-
  670    py_object_dir(ObjRef, List).
  671
  672py_obj_dict(ObjRef, Dict) :-
  673    py_object_dict(ObjRef, Dict).
 py_type(+ObjRef, -Type:atom) is det
True when Type is the name of the type of ObjRef. This is the same as type(ObjRef).__name__ in Python.
Compatibility
- PIP
  683py_type(ObjRef, Type) :-
  684    py_call(type(ObjRef):'__name__', Type).
 py_isinstance(+ObjRef, +Type) is semidet
True if ObjRef is an instance of Type or an instance of one of the sub types of Type. This is the same as isinstance(ObjRef) in Python.
Arguments:
Type- is either a term Module:Type or a plain atom to refer to a built-in type.
Compatibility
- PIP
  697py_isinstance(Obj, Module:Type) =>
  698    py_call(isinstance(Obj, eval(Module:Type)), @true).
  699py_isinstance(Obj, Type) =>
  700    py_call(isinstance(Obj, eval(sys:modules:'__getitem__'(builtins):Type)), @true).
 py_module_exists(+Module) is semidet
True if Module is a currently loaded Python module or it can be loaded.
Compatibility
- PIP
  709py_module_exists(Module) :-
  710    must_be(atom, Module),
  711    py_call(sys:modules:'__contains__'(Module), @true),
  712    !.
  713py_module_exists(Module) :-
  714    py_call(importlib:util:find_spec(Module), R),
  715    R \== @none,
  716    py_free(R).
 py_hasattr(+ModuleOrObj, ?Name) is nondet
True when Name is an attribute of Module. The name is derived from the Python built-in hasattr(). If Name is unbound, this enumerates the members of py_object_dir/2.
Arguments:
ModuleOrObj- If this is an atom it refers to a module, otherwise it must be a Python object reference.
Compatibility
- PIP
  729py_hasattr(ModuleOrObj, Name) :-
  730    var(Name),
  731    !,
  732    py_object_dir(ModuleOrObj, Names),
  733    member(Name, Names).
  734py_hasattr(ModuleOrObj, Name) :-
  735    must_be(atom, Name),
  736    (   atom(ModuleOrObj)
  737    ->  py_call(ModuleOrObj:'__name__'), % force loading
  738        py_call(hasattr(eval(sys:modules:'__getitem__'(ModuleOrObj)), Name), @true)
  739    ;   py_call(hasattr(ModuleOrObj, Name), @true)
  740    ).
 py_import(+Spec, +Options) is det
Import a Python module. Janus imports modules automatically when referred in py_call/2 and related predicates. Importing a module implies the module is loaded using Python's __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).
Errors
- permission_error(import_as, py_module, As) if there is already a module associated with As.
  778py_import(Spec, Options) :-
  779    option(as(_), Options),
  780    !,
  781    py_import_(Spec, Options).
  782py_import(Spec, Options) :-
  783    split_string(Spec, ".", "", Parts),
  784    last(Parts, Last),
  785    atom_string(As, Last),
  786    py_import_(Spec, [as(As)|Options]).
 py_module(+Module:atom, +Source:string) is det
Load Source into the Python module Module. This is intended to be used together with the 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.

Errors
- python_error(Type, Data) is raised if Python raises an error.
  809:- dynamic py_dyn_module/2 as volatile.  810
  811py_module(Module, Source) :-
  812    variant_sha1(Source, Hash),
  813    (   py_dyn_module(Module, Hash)
  814    ->  true
  815    ;   py_call(janus:import_module_from_string(Module, Source)),
  816        (   retract(py_dyn_module(Module, _))
  817        ->  py_update_module_cache(Module)
  818        ;   true
  819        ),
  820        asserta(py_dyn_module(Module, Hash))
  821    ).
  822
  823
  824		 /*******************************
  825		 *            INIT		*
  826		 *******************************/
  827
  828:- dynamic py_venv/2 as volatile.  829:- dynamic py_is_initialized/0 as volatile.  830
  831%   py_initialize is det.
  832%
  833%   Used as a callback from C for lazy initialization of Python.
  834
  835py_initialize :-
  836    getenv('VIRTUAL_ENV', VEnv),
  837    prolog_to_os_filename(VEnvDir, VEnv),
  838    atom_concat(VEnvDir, '/pyvenv.cfg', Cfg),
  839    venv_config(Cfg, Config),
  840    !,
  841    current_prolog_flag(executable, Program),
  842    current_prolog_flag(py_argv, Argv),
  843    py_initialize(Program, ['-I'|Argv], []),
  844    py_setattr(sys, prefix, VEnv),
  845    venv_update_path(VEnvDir, Config).
  846py_initialize :-
  847    current_prolog_flag(executable, Program),
  848    current_prolog_flag(py_argv, Argv),
  849    py_initialize(Program, Argv, []).
  850
  851venv_config(File, Config) :-
  852    access_file(File, read),
  853    read_file_to_string(File, String, []),
  854    split_string(String, "\n", "\n\r", Lines),
  855    convlist(venv_config_line, Lines, Config).
  856
  857venv_config_line(Line, Config) :-
  858    sub_string(Line, B, _, A, "="),
  859    !,
  860    sub_string(Line, 0, B, _, NameS),
  861    split_string(NameS, "", "\t\s", [NameS2]),
  862    atom_string(Name, NameS2),
  863    sub_string(Line, _, A, 0, ValueS),
  864    split_string(ValueS, "", "\t\s", [ValueS2]),
  865    (   number_string(Value, ValueS2)
  866    ->  true
  867    ;   atom_string(Value, ValueS2)
  868    ),
  869    Config =.. [Name,Value].
  870
  871venv_update_path(VEnvDir, Options) :-
  872    py_call(sys:version_info, Info),    % Tuple
  873    Info =.. [_,Major,Minor|_],
  874    format(string(EnvSiteDir),
  875           '~w/lib/python~w.~w/site-packages',
  876           [VEnvDir, Major, Minor]),
  877    prolog_to_os_filename(EnvSiteDir, PyEnvSiteDir),
  878    (   exists_directory(EnvSiteDir)
  879    ->  true
  880    ;   print_message(warning,
  881                      janus(venv(no_site_package_dir(VEnvDir, EnvSiteDir))))
  882    ),
  883    py_call(sys:path, Path0),
  884    (   option('include-system-site-packages'(true), Options)
  885    ->  partition(is_site_dir, Path0, PkgPath, SysPath),
  886        append([SysPath,[PyEnvSiteDir], PkgPath], Path)
  887    ;   exclude(is_site_dir, Path0, Path1),
  888        append(Path1, [PyEnvSiteDir], Path)
  889    ),
  890    py_setattr(sys, path, Path),
  891    print_message(silent, janus(venv(VEnvDir, EnvSiteDir))),
  892    asserta(py_venv(VEnvDir, EnvSiteDir)).
  893
  894is_site_dir(OsDir) :-
  895    prolog_to_os_filename(PlDir, OsDir),
  896    file_base_name(PlDir, Dir0),
  897    downcase_atom(Dir0, Dir),
  898    no_env_dir(Dir).
  899
  900no_env_dir('site-packages').
  901no_env_dir('dist-packages').
 py_initialize(+Program, +Argv, +Options) is det
Initialize and configure the embedded Python system. If this predicate is not called before any other call to Python such as py_call/2, it is called lazily, passing the Prolog executable as Program, passing Argv from the Prolog flag 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

Arguments:
Options- is currently ignored. It will be used to provide additional configuration options.
  925py_initialize(Program, Argv, Options) :-
  926    (   py_initialize_(Program, Argv, Options)
  927    ->  absolute_file_name(library('python/janus.py'), Janus,
  928			   [ access(read) ]),
  929	file_directory_name(Janus, PythonDir),
  930	py_add_lib_dir(PythonDir, first),
  931	py_connect_io,
  932        repl_add_cwd,
  933        asserta(py_is_initialized)
  934    ;   true
  935    ).
 py_connect_io is det
If SWI-Prolog console streams are bound to something non-standard, bind the Python console I/O to our streans.
  942py_connect_io :-
  943    maplist(non_file_stream,
  944	    [0-user_input, 1-user_output, 2-user_error],
  945	    NonFiles),
  946    Call =.. [connect_io|NonFiles],
  947    py_call(janus_swi:Call).
  948
  949non_file_stream(Expect-Stream, Bool) :-
  950    (   stream_property(Stream, file_no(Expect))
  951    ->  Bool = @false
  952    ;   Bool = @true
  953    ).
  954
  955		 /*******************************
  956		 *            PATHS		*
  957		 *******************************/
 py_lib_dirs(-Dirs) is det
True when Dirs is a list of directories searched for Python modules. The elements of Dirs are in Prolog canonical notation.
Compatibility
- PIP
  966py_lib_dirs(Dirs) :-
  967    py_call(sys:path, Dirs0),
  968    maplist(prolog_to_os_filename, Dirs, Dirs0).
 py_add_lib_dir(+Dir) is det
 py_add_lib_dir(+Dir, +Where) is det
Add a directory to the Python module search path. In the second form, Where is one of 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.

Compatibility
- PIP. Note that SWI-Prolog uses POSIX file conventions internally, mapping to OS conventions inside the predicates that deal with files or explicitly using prolog_to_os_filename/2. Other systems may use the native file conventions in Prolog.
  990:- multifile system:term_expansion/2.  991
  992system:term_expansion((:- py_add_lib_dir(Dir0)), Directive) :-
  993    system:term_expansion((:- py_add_lib_dir(Dir0, last)), Directive).
  994system:term_expansion((:- py_add_lib_dir(Dir0, Where)),
  995                      (:- initialization(py_add_lib_dir(Dir, Where), now))) :-
  996    \+ (atomic(Dir0), is_absolute_file_name(Dir0)),
  997    prolog_load_context(directory, CWD),
  998    absolute_file_name(Dir0, Dir,
  999                       [ relative_to(CWD),
 1000                         file_type(directory),
 1001                         access(read)
 1002                       ]).
 1003
 1004py_add_lib_dir(Dir) :-
 1005    py_add_lib_dir(Dir, last).
 1006
 1007py_add_lib_dir(Dir, Where) :-
 1008    atomic(Dir),
 1009    !,
 1010    absolute_file_name(Dir, AbsDir),
 1011    prolog_to_os_filename(AbsDir, OSDir),
 1012    py_add_lib_dir_(OSDir, Where).
 1013py_add_lib_dir(Alias, Where) :-
 1014    absolute_file_name(Alias, AbsDir,
 1015                       [ file_type(directory),
 1016                         access(read)
 1017                       ]),
 1018    prolog_to_os_filename(AbsDir, OSDir),
 1019    py_add_lib_dir_(OSDir, Where).
 1020
 1021py_add_lib_dir_(OSDir, Where) :-
 1022    (   py_call(sys:path, Dirs0),
 1023        memberchk(OSDir, Dirs0)
 1024    ->  true
 1025    ;   Where == last
 1026    ->  py_call(sys:path:append(OSDir), _)
 1027    ;   Where == first
 1028    ->  py_call(sys:path:insert(0, OSDir), _)
 1029    ;   must_be(oneof([first,last]), Where)
 1030    ).
 1031
 1032:- det(repl_add_cwd/0). 1033repl_add_cwd :-
 1034    current_prolog_flag(break_level, Level),
 1035    Level >= 0,
 1036    !,
 1037    (   py_call(sys:path:count(''), N),
 1038        N > 0
 1039    ->  true
 1040    ;   print_message(informational, janus(add_cwd)),
 1041        py_add_lib_dir_('', first)
 1042    ).
 1043repl_add_cwd.
 1044
 1045:- multifile
 1046    prolog:repl_loop_hook/2. 1047
 1048prolog:repl_loop_hook(begin, Level) :-
 1049    Level >= 0,
 1050    py_is_initialized,
 1051    repl_add_cwd.
 1052
 1053
 1054		 /*******************************
 1055		 *           CALLBACK		*
 1056		 *******************************/
 1057
 1058:- dynamic py_call_cache/8 as volatile. 1059
 1060:- meta_predicate py_call_string(:, +, -). 1061
 1062%   py_call_string(:String, +DictIn, -Dict) is nondet.
 1063%
 1064%   Support janus.query_once() and janus.query(). Parses   String  into a goal
 1065%   term. Next, all variables from the goal   term that appear in DictIn
 1066%   are bound to the value from  this   dict.  Dict  is created from the
 1067%   remaining variables, unless they  start   with  an underscore (e.g.,
 1068%   `_Time`) and the key `truth. On   success,  the Dict values contain
 1069%   the bindings from the  answer  and   `truth`  is  either  `true` or
 1070%   `Undefined`. On failure, the Dict values are bound to `None` and the
 1071%   `truth` is `false`.
 1072%
 1073%   Parsing and distributing the variables over the two dicts is cached.
 1074
 1075py_call_string(M:String, Input, Dict) :-
 1076    py_call_cache(String, Input, TV, M, Goal, Dict, Truth, OutVars),
 1077    !,
 1078    py_call(TV, M:Goal, Truth, OutVars).
 1079py_call_string(M:String, Input, Dict) :-
 1080    term_string(Goal, String, [variable_names(Map)]),
 1081    unbind_dict(Input, VInput),
 1082    exclude(not_in_projection(VInput), Map, OutBindings),
 1083    dict_create(Dict, bindings, [truth=Truth|OutBindings]),
 1084    maplist(arg(2), OutBindings, OutVars),
 1085    TV = Input.get(truth, 'PLAIN_TRUTHVALS'),
 1086    asserta(py_call_cache(String, VInput, TV, M, Goal, Dict, Truth, OutVars)),
 1087    VInput = Input,
 1088    py_call(TV, M:Goal, Truth, OutVars).
 1089
 1090py_call('NO_TRUTHVALS', M:Goal, Truth, OutVars) =>
 1091    (   call(M:Goal)
 1092    *-> bind_status_no_no_truthvals(Truth)
 1093    ;   Truth = @false,
 1094	maplist(bind_none, OutVars)
 1095    ).
 1096py_call('PLAIN_TRUTHVALS', M:Goal, Truth, OutVars) =>
 1097    (   call(M:Goal)
 1098    *-> bind_status_plain_truthvals(Truth)
 1099    ;   Truth = @false,
 1100	maplist(bind_none, OutVars)
 1101    ).
 1102py_call('DELAY_LISTS', M:Goal, Truth, OutVars) =>
 1103    (   call_delays(M:Goal, Delays)
 1104    *-> bind_status_delay_lists(Delays, Truth)
 1105    ;   Truth = @false,
 1106	maplist(bind_none, OutVars)
 1107    ).
 1108py_call('RESIDUAL_PROGRAM', M:Goal, Truth, OutVars) =>
 1109    (   call_delays(M:Goal, Delays)
 1110    *-> bind_status_residual_program(Delays, Truth)
 1111    ;   Truth = @false,
 1112	maplist(bind_none, OutVars)
 1113    ).
 1114
 1115not_in_projection(Input, Name=Value) :-
 1116    (   get_dict(Name, Input, Value)
 1117    ->  true
 1118    ;   sub_atom(Name, 0, _, _, '_')
 1119    ).
 1120
 1121bind_none(@none).
 1122
 1123bind_status_no_no_truthvals(@true).
 1124
 1125bind_status_plain_truthvals(Truth) =>
 1126    (   '$tbl_delay_list'([])
 1127    ->  Truth = @true
 1128    ;   py_undefined(Truth)
 1129    ).
 1130
 1131bind_status_delay_lists(true, Truth) =>
 1132    Truth = @true.
 1133bind_status_delay_lists(Delays, Truth) =>
 1134    py_call(janus:'Undefined'(prolog(Delays)), Truth).
 1135
 1136bind_status_residual_program(true, Truth) =>
 1137    Truth = @true.
 1138bind_status_residual_program(Delays, Truth) =>
 1139    delays_residual_program(Delays, Program),
 1140    py_call(janus:'Undefined'(prolog(Program)), Truth).
 1141
 1142py_undefined(X) :-
 1143    py_call(janus:undefined, X).
 1144
 1145unbind_dict(Dict0, Dict) :-
 1146    dict_pairs(Dict0, Tag, Pairs0),
 1147    maplist(unbind, Pairs0, Pairs),
 1148    dict_pairs(Dict, Tag, Pairs).
 1149
 1150unbind(Name-_, Name-_) :-
 1151    sub_atom(Name, 0, 1, _, Char1),
 1152    char_type(Char1, prolog_var_start),
 1153    !.
 1154unbind(NonVar, NonVar).
 1155
 1156
 1157		 /*******************************
 1158		 *     SUPPORT PYTHON CALLS     *
 1159		 *******************************/
 1160
 1161:- public
 1162       px_cmd/3,
 1163       px_call/4,
 1164       px_comp/7. 1165
 1166% These predicates are helpers  for the corresponding Python functions
 1167% in janus.py.
 1168
 1169
 1170%   px_call(+Input:tuple, +Module, -Pred, -Ret)
 1171%
 1172%   Supports  px_qdet()  and  apply().  Note    that   these  predicates
 1173%   explicitly address predicates  in  a   particular  module.  For meta
 1174%   predicates, this implies they also control  the context module. This
 1175%   leads to ``janus.cmd("consult", "consult", file)`` to consult _file_
 1176%   into the module `consult`, which is not   what we want. Therefore we
 1177%   set the context module to `user`, which is better, but probably also
 1178%   not what we want.
 1179
 1180px_call(-(), Module, Pred, Ret) =>
 1181    @(call(Module:Pred, Ret), user).
 1182px_call(-(A1), Module, Pred, Ret) =>
 1183    @(call(Module:Pred, A1, Ret), user).
 1184px_call(-(A1,A2), Module, Pred, Ret) =>
 1185    @(call(Module:Pred, A1, A2, Ret), user).
 1186px_call(-(A1,A2,A3), Module, Pred, Ret) =>
 1187    @(call(Module:Pred, A1, A2, A3, Ret), user).
 1188px_call(-(A1,A2,A3,A4), Module, Pred, Ret) =>
 1189    @(call(Module:Pred, A1, A2, A3, A4, Ret), user).
 1190px_call(Tuple, Module, Pred, Ret) =>
 1191    compound_name_arguments(Tuple, _, Args),
 1192    append(Args, [Ret], GArgs),
 1193    Goal =.. [Pred|GArgs],
 1194    @(Module:Goal, user).
 1195
 1196px_cmd(Module, Pred, Tuple) :-
 1197    (   compound(Tuple)
 1198    ->  compound_name_arguments(Tuple, _, Args),
 1199	Goal =.. [Pred|Args]
 1200    ;   Goal = Pred
 1201    ),
 1202    @(Module:Goal, user).
 1203
 1204px_comp(Module, Pred, Tuple, Vars, Set, TV, Ret) :-
 1205    length(Out, Vars),
 1206    (   compound(Tuple)
 1207    ->  compound_name_arguments(Tuple, _, Args),
 1208	append(Args, Out, GArgs),
 1209	Goal =.. [Pred|GArgs]
 1210    ;   Goal =.. [Pred|Out]
 1211    ),
 1212    compound_name_arguments(OTempl0, -, Out),
 1213    tv_goal_and_template(TV, @(Module:Goal, user), FGoal, OTempl0, OTempl),
 1214    findall(OTempl, FGoal, Ret0),
 1215    (   Set == @true
 1216    ->  sort(Ret0, Ret)
 1217    ;   Ret = Ret0
 1218    ).
 1219
 1220:- meta_predicate
 1221    call_delays_py(0, -). 1222
 1223% 0,1,2: TruthVal(Enum) from janus.py
 1224tv_goal_and_template('NO_TRUTHVALS',
 1225                     Goal, Goal, Templ, Templ) :- !.
 1226tv_goal_and_template('PLAIN_TRUTHVALS',
 1227                     Goal, ucall(Goal, TV), Templ, -(Templ,TV)) :- !.
 1228tv_goal_and_template('DELAY_LISTS',
 1229                     Goal, call_delays_py(Goal, TV), Templ, -(Templ,TV)) :- !.
 1230tv_goal_and_template(Mode, _, _, _, _) :-
 1231    domain_error("px_comp() truth", Mode).
 1232
 1233:- public
 1234    ucall/2,
 1235    call_delays_py/2. 1236
 1237ucall(Goal, TV) :-
 1238    call(Goal),
 1239    (   '$tbl_delay_list'([])
 1240    ->  TV = 1
 1241    ;   TV = 2
 1242    ).
 1243
 1244call_delays_py(Goal, PyDelays) :-
 1245    call_delays(Goal, Delays),
 1246    (   Delays == true
 1247    ->  PyDelays = []
 1248    ;   comma_list(Delays, Array),
 1249        maplist(term_string, Array, PyDelays)
 1250    ).
 1251
 1252
 1253		 /*******************************
 1254		 *          PYTHON I/O          *
 1255		 *******************************/
 1256
 1257%   py_write(+Stream, -String) is det.
 1258%   py_readline(+Stream, +Size, +Prompt, +Line) is det.
 1259%
 1260%   Called from redefined Python console  I/O   to  write/read using the
 1261%   Prolog streams.
 1262
 1263:- '$hide'((py_write/1,
 1264	    py_readline/4)). 1265
 1266py_write(Stream, String) :-
 1267    notrace(format(Stream, '~s', [String])).
 1268
 1269py_readline(Stream, Size, Prompt, Line) :-
 1270    notrace(py_readline_(Stream, Size, Prompt, Line)).
 1271
 1272py_readline_(Stream, _Size, Prompt, Line) :-
 1273    prompt1(Prompt),
 1274    read_line_to_string(Stream, Read),
 1275    (   Read == end_of_file
 1276    ->  Line = ""
 1277    ;   string_concat(Read, "\n", Line),
 1278	py_add_history(Read)
 1279    ).
 1280
 1281py_add_history(Line) :-
 1282    ignore(catch(prolog:history(user_input, add(Line)), _, true)).
 1283
 1284
 1285		 /*******************************
 1286		 *          COMPILING           *
 1287		 *******************************/
 1288
 1289%   py_consult(+File, +Data, +Module) is det.
 1290%
 1291%   Support janus.consult(file, data=None, module='user').
 1292
 1293:- public py_consult/3. 1294py_consult(File, @none, Module) =>
 1295    consult(Module:File).
 1296py_consult(File, Data, Module) =>
 1297    setup_call_cleanup(
 1298	open_string(Data, In),
 1299	load_files(Module:File, [stream(In)]),
 1300	close(In)).
 1301
 1302
 1303		 /*******************************
 1304		 *           MESSAGES		*
 1305		 *******************************/
 1306
 1307:- multifile
 1308    prolog:error_message//1,
 1309    prolog:message_context//1,
 1310    prolog:message//1. 1311
 1312prolog:error_message(python_error(Class, Value)) -->
 1313    { py_str(Value, Message)
 1314    },
 1315    [ 'Python ', ansi(code, "'~w'", [Class]), ':', nl,
 1316      '  ~w'-[Message]
 1317    ].
 1318prolog:error_message(permission_error(import_as, py_module, As)) -->
 1319    [ 'Janus: No permission to import a module as ', ansi(code, '~q', As),
 1320      ': module exists.'
 1321    ].
 1322
 1323prolog:message_context(context(_, PythonCtx)) -->
 1324    { nonvar(PythonCtx),
 1325      PythonCtx = python_stack(Stack),
 1326      current_prolog_flag(py_backtrace, true),
 1327      py_is_object(Stack),
 1328      !,
 1329      current_prolog_flag(py_backtrace_depth, Depth),
 1330      py_call(traceback:format_tb(Stack, Depth), Frames)
 1331    },
 1332    [ nl, 'Python stack:', nl ],
 1333    sequence(py_stack_frame, Frames).
 1334
 1335py_stack_frame(String) -->
 1336    { split_string(String, "\n", "", Lines)
 1337    },
 1338    sequence(msg_line, [nl], Lines).
 1339
 1340msg_line(Line) -->
 1341    [ '~s'-[Line] ].
 1342
 1343prolog:message(janus(Msg)) -->
 1344    message(Msg).
 1345
 1346message(version(Janus, Python)) -->
 1347    [ 'Janus ~w embeds Python ~w'-[Janus, Python] ].
 1348message(venv(Dir, _EnvSiteDir)) -->
 1349    [ 'Janus: using venv from ~p'-[Dir] ].
 1350message(venv(no_site_package_dir(VEnvDir, Dir))) -->
 1351    [ 'Janus: venv dirrectory ~p does not contain ~p'-[VEnvDir, Dir] ].
 1352message(py_shell(no_janus)) -->
 1353    [ 'Janus: py_shell/0: Importing janus into the Python shell requires Python 3.10 or later.', nl,
 1354      'Run "', ansi(code, 'from janus import *', []), '" in the Python shell to import janus.'
 1355    ].
 1356message(add_cwd) -->
 1357    [ 'Interactive session; added `.` to Python `sys.path`'-[] ]