View source with formatted 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)]).  139
  140/** <module> Call Python from Prolog
  141
  142This library implements calling Python  from   Prolog.  It  is available
  143directly from Prolog if  the  janus   package  is  bundled.  The library
  144provides access to an  _embedded_  Python   instance.  If  SWI-Prolog is
  145embedded into Python  using  the   Python  package  ``janus-swi``,  this
  146library is provided either from Prolog or from the Python package.
  147
  148Normally,  the  Prolog  user  can  simply  start  calling  Python  using
  149py_call/2 or friends. In special cases it   may  be needed to initialize
  150Python with options using  py_initialize/3   and  optionally  the Python
  151search path may be extended using py_add_lib_dir/1.
  152*/
  153
  154%!  py_version is det.
  155%
  156%   Print version  info on the  embedded Python installation  based on
  157%   Python `sys.version`.  If a Python _virtual environment_ (venv) is
  158%   active, indicate this with the location of this environment found.
  159
  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    ).
  168
  169
  170%!  py_call(+Call) is det.
  171%!  py_call(+Call, -Return) is det.
  172%!  py_call(+Call, -Return, +Options) is det.
  173%
  174%   Call Python and return the result of   the called function. Call has
  175%   the shape `[Target][:Action]*`, where `Target`   is  either a Python
  176%   module name or a Python object reference. Each `Action` is either an
  177%   atom to get the denoted attribute from   current `Target` or it is a
  178%   compound term where the first  argument   is  the function or method
  179%   name  and  the  arguments  provide  the  parameters  to  the  Python
  180%   function. On success, the returned Python   object  is translated to
  181%   Prolog.  `Action` without a `Target` denotes a buit-in function.
  182%
  183%   Arguments to Python  functions  use   the  Python  conventions. Both
  184%   _positional_  and  _keyword_  arguments    are   supported.  Keyword
  185%   arguments are written as `Name = Value`   and  must appear after the
  186%   positional arguments.
  187%
  188%   Below are some examples.
  189%
  190%       % call a built-in
  191%	?- py_call(print("Hello World!\n")).
  192%	true.
  193%
  194%       % call a built-in (alternative)
  195%	?- py_call(builtins:print("Hello World!\n")).
  196%	true.
  197%
  198%	% call function in a module
  199%	?- py_call(sys:getsizeof([1,2,3]), Size).
  200%	Size = 80.
  201%
  202%	% call function on an attribute of a module
  203%       ?- py_call(sys:path:append("/home/bob/janus")).
  204%       true
  205%
  206%       % get attribute from a module
  207%       ?- py_call(sys:path, Path)
  208%       Path = ["dir1", "dir2", ...]
  209%
  210%       % reference a Python builtin (class or function)
  211%       ?- py_call(builtins:list, L, [py_object(true)]).
  212%       L = <py>(...,type).
  213%
  214%   Given a class in a file `dog.py`  such as the following example from
  215%   the Python documentation
  216%
  217%   ```
  218%   class Dog:
  219%       tricks = []
  220%
  221%       def __init__(self, name):
  222%           self.name = name
  223%
  224%       def add_trick(self, trick):
  225%           self.tricks.append(trick)
  226%   ```
  227%
  228%   We can interact with this class as  below. Note that ``$Doc`` in the
  229%   SWI-Prolog toplevel refers to the  last   toplevel  binding  for the
  230%   variable `Dog`.
  231%
  232%       ?- py_call(dog:'Dog'("Fido"), Dog).
  233%       Dog = <py>(0x7f095c9d02e0,'Dog').
  234%
  235%       ?- py_call($Dog:add_trick("roll_over")).
  236%       Dog = <py>(0x7f095c9d02e0,'Dog').
  237%
  238%       ?- py_call($Dog:tricks, Tricks).
  239%       Dog = <py>(0x7f095c9d02e0,'Dog'),
  240%       Tricks = ["roll_over"]
  241%
  242%   If the principal term of the   first  argument is not `Target:Func`,
  243%   The argument is evaluated as the initial target, i.e., it must be an
  244%   object reference or a module.   For example:
  245%
  246%       ?- py_call(dog:'Dog'("Fido"), Dog),
  247%          py_call(Dog, X).
  248%          Dog = X, X = <py>(0x7fa8cbd12050,'Dog').
  249%       ?- py_call(sys, S).
  250%          S = <py>(0x7fa8cd582390,module).
  251%
  252%   Options processed:
  253%
  254%     - py_object(Boolean)
  255%       If `true` (default `false`), translate the return as a Python
  256%       object reference. Some objects are _always_ translated to
  257%       Prolog, regardless of this flag.  These are the Python constants
  258%       ``None``, ``True`` and ``False`` as well as instances of the
  259%       Python base classes `int`, `float`, `str` or `tuple`. Instances
  260%       of sub classes of these base classes are controlled by this
  261%       option.
  262%     - py_string_as(+Type)
  263%       If Type is `atom` (default), translate a Python String into a
  264%       Prolog atom.  If Type is `string`, translate into a Prolog string.
  265%	Strings are more efficient if they are short lived.
  266%     - py_dict_as(+Type)
  267%       One of `dict` (default) to map a Python dict to a SWI-Prolog
  268%       dict if all keys can be represented.  If `{}` or not all keys
  269%       can be represented, Return is unified to a term `{k:v, ...}`
  270%       or `py({})` if the Python dict is empty.
  271%
  272%   @compat  PIP.  The  options  `py_string_as`   and  `py_dict_as`  are
  273%   SWI-Prolog  specific,  where  SWI-Prolog   Janus  represents  Python
  274%   strings as atoms as required by  the   PIP  and it represents Python
  275%   dicts by default  as  SWI-Prolog   dicts.  The  predicates values/3,
  276%   keys/2, etc. provide portable access to the data in the dict.
  277
  278%!  py_iter(+Iterator, -Value) is nondet.
  279%!  py_iter(+Iterator, -Value, +Options) is nondet.
  280%
  281%   True when Value is returned by the Python Iterator. Python iterators
  282%   may be used to implement   non-deterministic foreign predicates. The
  283%   implementation uses these steps:
  284%
  285%     1. Evaluate Iterator as py_call/2 evaluates its first argument,
  286%        except the ``Obj:Attr = Value`` construct is not accepted.
  287%     2. Call ``__iter__`` on the result to get the iterator itself.
  288%     3. Get the ``__next__`` function of the iterator.
  289%     4. Loop over the return values of the _next_ function.  If
  290%        the Python return value unifies with Value, succeed with
  291%        a choicepoint.  Abort on Python or unification exceptions.
  292%     5. Re-satisfaction continues at (4).
  293%
  294%   The example below uses the built-in iterator range():
  295%
  296%       ?- py_iter(range(1,3), X).
  297%       X = 1 ;
  298%       X = 2.
  299%
  300%   Note that the implementation performs a   _look  ahead_, i.e., after
  301%   successful unification it calls `__next__()`   again. On failure the
  302%   Prolog predicate succeeds deterministically. On   success,  the next
  303%   candidate is stored.
  304%
  305%   Note that a Python _generator_ is   a  Python _iterator_. Therefore,
  306%   given  the  Python  generator   expression    below,   we   can  use
  307%   py_iter(squares(1,5),X) to generate the squares on backtracking.
  308%
  309%   ```
  310%   def squares(start, stop):
  311%        for i in range(start, stop):
  312%            yield i * i
  313%   ```
  314%
  315%   @arg Options is processed as with py_call/3.
  316%   @bug Iterator may not depend on janus.query(), i.e., it is not
  317%   possible to iterate over a Python iterator that under the hoods
  318%   relies on a Prolog non-deterministic predicate.
  319%   @compat PIP.  The same remarks as for py_call/2 apply.
  320
  321%!  py_setattr(+Target, +Name, +Value) is det.
  322%
  323%   Set a Python attribute on an object.  If   Target  is an atom, it is
  324%   interpreted  as  a  module.  Otherwise  it  is  normally  an  object
  325%   reference. py_setattr/3 allows for  _chaining_   and  behaves  as if
  326%   defined as
  327%
  328%       py_setattr(Target, Name, Value) :-
  329%           py_call(Target, Obj, [py_object(true)]),
  330%           py_call(setattr(Obj, Name, Value)).
  331%
  332%   @compat PIP
  333
  334%!  py_run(+String, +Globals, +Locals, -Result, +Options) is det.
  335%
  336%   Interface  to  Py_CompileString()  followed   by  PyEval_EvalCode().
  337%   Options:
  338%
  339%       - file_name(String)
  340%         Errors are reported against this pseudo file name
  341%       - start(Token)
  342%         One of `eval`, `file` (default) or `single`.
  343%
  344%   @arg Globals is a dict
  345%   @arg Locals is a dict
  346
  347%!  py_is_object(@Term) is semidet.
  348%
  349%   True when Term is a Python object reference. Fails silently if Term
  350%   is any other Prolog term.
  351%
  352%   @error existence_error(py_object, Term) is raised of Term is a
  353%   Python object, but it has been freed using py_free/1.
  354%
  355%   @compat PIP. The SWI-Prolog implementation is safe in the sense that
  356%   an arbitrary term cannot be confused  with   a  Python  object and a
  357%   reliable error is generated  if  the   references  has  been  freed.
  358%   Portable applications can not rely on this.
  359
  360%!  py_is_dict(@Term) is semidet.
  361%
  362%   True if Term is a Prolog term that represents a Python dict.
  363%
  364%   @compat PIP. The SWI-Prolog version accepts   both a SWI-Prolog dict
  365%   and the `{k:v,...}`  representation.  See   `py_dict_as`  option  of
  366%   py_call/2.
  367
  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).
  375
  376
  377%!  py_free(+Obj) is det.
  378%
  379%   Immediately free (decrement the  reference   count)  for  the Python
  380%   object Obj. Further reference  to  Obj   using  e.g.,  py_call/2  or
  381%   py_free/1 raises an `existence_error`. Note that by decrementing the
  382%   reference count, we make the reference invalid from Prolog. This may
  383%   not  actually  delete  the  object  because   the  object  may  have
  384%   references inside Python.
  385%
  386%   Prolog references to Python objects  are   subject  to  atom garbage
  387%   collection and thus normally do not need to be freed explicitly.
  388%
  389%   @compat PIP. The SWI-Prolog  implementation   is  safe  and normally
  390%   reclaiming Python object can  be  left   to  the  garbage collector.
  391%   Portable applications may not assume   garbage  collection of Python
  392%   objects and must ensure to call py_free/1 exactly once on any Python
  393%   object reference. Not calling  py_free/1   leaks  the Python object.
  394%   Calling it twice may lead to undefined behavior.
  395
  396%!  py_with_gil(:Goal) is semidet.
  397%
  398%   Run Goal as  once(Goal)  while  holding   the  Phyton  GIL  (_Global
  399%   Interpreter Lock_). Note that  all   predicates  that  interact with
  400%   Python lock the GIL. This predicate is   only required if we wish to
  401%   make multiple calls to Python while keeping   the  GIL. The GIL is a
  402%   _recursive_ lock and thus calling py_call/1,2  while holding the GIL
  403%   does not _deadlock_.
  404
  405%!  py_gil_owner(-Thread) is semidet.
  406%
  407%   True when  the Python GIL is  owned by Thread.  Note  that, unless
  408%   Thread  is the  calling thread,  this merely  samples the  current
  409%   state and may thus no longer  be true when the predicate succeeds.
  410%   This predicate is intended to help diagnose _deadlock_ problems.
  411%
  412%   Note that  this predicate returns  the Prolog threads  that locked
  413%   the GIL.  It is however possible that Python releases the GIL, for
  414%   example if  it performs a  blocking call.  In this  scenario, some
  415%   other thread or no thread may hold the gil.
  416
  417
  418		 /*******************************
  419		 *         COMPATIBILIY		*
  420		 *******************************/
  421
  422%!  py_func(+Module, +Function, -Return) is det.
  423%!  py_func(+Module, +Function, -Return, +Options) is det.
  424%
  425%   Call Python Function in  Module.   The  SWI-Prolog implementation is
  426%   equivalent to py_call(Module:Function, Return).   See  py_call/2 for
  427%   details.
  428%
  429%   @compat  PIP.  See  py_call/2  for  notes.    Note   that,  as  this
  430%   implementation is based on py_call/2,   Function can use _chaining_,
  431%   e.g., py_func(sys, path:append(dir), Return)  is   accepted  by this
  432%   implementation, but not portable.
  433
  434py_func(Module, Function, Return) :-
  435    py_call(Module:Function, Return).
  436py_func(Module, Function, Return, Options) :-
  437    py_call(Module:Function, Return, Options).
  438
  439%!  py_dot(+ObjRef, +MethAttr, -Ret) is det.
  440%!  py_dot(+ObjRef, +MethAttr, -Ret, +Options) is det.
  441%
  442%   Call a method or access  an  attribute   on  the  object ObjRef. The
  443%   SWI-Prolog implementation is equivalent  to py_call(ObjRef:MethAttr,
  444%   Return). See py_call/2 for details.
  445%
  446%   @compat PIP.  See py_func/3 for details.
  447
  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		 *******************************/
  457
  458%!  values(+Dict, +Path, ?Val) is semidet.
  459%
  460%   Get the value associated with Dict at  Path. Path is either a single
  461%   key or a list of keys.
  462%
  463%   @compat PIP. Note that this predicate   handle  a SWI-Prolog dict, a
  464%   {k:v, ...} term as well as py({k:v, ...}.
  465
  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).
  494
  495%!  keys(+Dict, ?Keys) is det.
  496%
  497%   True when Keys is a list of keys that appear in Dict.
  498%
  499%   @compat PIP. Note that this predicate   handle  a SWI-Prolog dict, a
  500%   {k:v, ...} term as well as py({k:v, ...}.
  501
  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].
  514
  515%!  key(+Dict, ?Key) is nondet.
  516%
  517%   True when Key is a key in   Dict.  Backtracking enumerates all known
  518%   keys.
  519%
  520%   @compat PIP. Note that this predicate   handle  a SWI-Prolog dict, a
  521%   {k:v, ...} term as well as py({k:v, ...}.
  522
  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).
  534
  535%!  items(+Dict, ?Items) is det.
  536%
  537%   True when Items is a list of Key:Value that appear in Dict.
  538%
  539%   @compat PIP. Note that this predicate   handle  a SWI-Prolog dict, a
  540%   {k:v, ...} term as well as py({k:v, ...}.
  541
  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		 *******************************/
  562
  563%!  py_shell
  564%
  565%   Start an interactive Python REPL  loop   using  the  embedded Python
  566%   interpreter. The interpreter first imports `janus` as below.
  567%
  568%       from janus import *
  569%
  570%   So, we can do
  571%
  572%       ?- py_shell.
  573%       ...
  574%       >>> query_once("writeln(X)", {"X":"Hello world"})
  575%       Hello world
  576%       {'truth': True}
  577%
  578%   If possible, we enable command line   editing using the GNU readline
  579%   library.
  580%
  581%   When used in an environment  where  Prolog   does  not  use the file
  582%   handles 0,1,2 for  the  standard   streams,  e.g.,  in  `swipl-win`,
  583%   Python's I/O is rebound to use  Prolog's I/O. This includes Prolog's
  584%   command line editor, resulting in  a   mixed  history  of Prolog and
  585%   Pythin commands.
  586
  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		 *******************************/
  603
  604%!  py_pp(+Term) is det.
  605%!  py_pp(+Term, +Options) is det.
  606%!  py_pp(+Stream, +Term, +Options) is det.
  607%
  608%   Pretty prints the Prolog translation of a Python data structure in
  609%   Python  syntax. This  exploits  pformat() from  the Python  module
  610%   `pprint` to do the actual  formatting.  Options is translated into
  611%   keyword arguments  passed to  pprint.pformat().  In  addition, the
  612%   option  nl(Bool)  is processed.   When  `true`  (default), we  use
  613%   pprint.pp(), which  makes the output  followed by a  newline.  For
  614%   example:
  615%
  616%   ```
  617%   ?- py_pp(py{a:1, l:[1,2,3], size:1000000},
  618%            [underscore_numbers(true)]).
  619%   {'a': 1, 'l': [1, 2, 3], 'size': 1_000_000}
  620%   ```
  621%
  622%   @compat PIP
  623
  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).
  647
  648
  649%!  py_object_dir(+ObjRef, -List) is det.
  650%!  py_object_dict(+ObjRef, -Dict) is det.
  651%
  652%   Examine attributes of  an  object.   The  predicate  py_object_dir/2
  653%   fetches the names of all attributes,   while  py_object_dir/2 gets a
  654%   dict with all attributes and their values.
  655%
  656%   @compat PIP
  657
  658py_object_dir(ObjRef, List) :-
  659    py_call(ObjRef:'__dir__'(), List).
  660
  661py_object_dict(ObjRef, Dict) :-
  662    py_call(ObjRef:'__dict__', Dict).
  663
  664%!  py_obj_dir(+ObjRef, -List) is det.
  665%!  py_obj_dict(+ObjRef, -Dict) is det.
  666%
  667%   @deprecated Use py_object_dir/2 or py_object_dict/2.
  668
  669py_obj_dir(ObjRef, List) :-
  670    py_object_dir(ObjRef, List).
  671
  672py_obj_dict(ObjRef, Dict) :-
  673    py_object_dict(ObjRef, Dict).
  674
  675
  676%!  py_type(+ObjRef, -Type:atom) is det.
  677%
  678%   True when Type is the name of the   type of ObjRef. This is the same
  679%   as ``type(ObjRef).__name__`` in Python.
  680%
  681%   @compat PIP
  682
  683py_type(ObjRef, Type) :-
  684    py_call(type(ObjRef):'__name__', Type).
  685
  686%!  py_isinstance(+ObjRef, +Type) is semidet.
  687%
  688%   True if ObjRef is an instance of Type   or an instance of one of the
  689%   sub types of Type. This  is   the  same as ``isinstance(ObjRef)`` in
  690%   Python.
  691%
  692%   @arg Type is either a term `Module:Type` or a plain atom to refer to
  693%   a built-in type.
  694%
  695%   @compat PIP
  696
  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).
  701
  702%!  py_module_exists(+Module) is semidet.
  703%
  704%   True if Module is a currently  loaded   Python  module  or it can be
  705%   loaded.
  706%
  707%   @compat PIP
  708
  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).
  717
  718%!  py_hasattr(+ModuleOrObj, ?Name) is nondet.
  719%
  720%   True when Name is an attribute of   Module. The name is derived from
  721%   the Python built-in hasattr(). If Name   is unbound, this enumerates
  722%   the members of py_object_dir/2.
  723%
  724%   @arg ModuleOrObj If this is an atom it refers to a module, otherwise
  725%   it must be a Python object reference.
  726%
  727%   @compat PIP
  728
  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    ).
  741
  742
  743%!  py_import(+Spec, +Options) is det.
  744%
  745%   Import a Python module.  Janus   imports  modules automatically when
  746%   referred in py_call/2 and  related   predicates.  Importing a module
  747%   implies  the  module  is  loaded   using  Python's  ``__import__()``
  748%   built-in and added to a table  that   maps  Prolog atoms to imported
  749%   modules. This predicate explicitly imports a module and allows it to
  750%   be associated with a different  name.   This  is  useful for loading
  751%   _nested modules_, i.e., a specific module   from a Python package as
  752%   well as for  avoiding  conflicts.  For   example,  with  the  Python
  753%   `selenium` package installed, we can do in Python:
  754%
  755%       >>> from selenium import webdriver
  756%       >>> browser = webdriver.Chrome()
  757%
  758%   Without this predicate, we can do
  759%
  760%       ?- py_call('selenium.webdriver':'Chrome'(), Chrome).
  761%
  762%   For a single call this is  fine,   but  for making multiple calls it
  763%   gets cumbersome.  With this predicate we can write this.
  764%
  765%       ?- py_import('selenium.webdriver', []).
  766%       ?- py_call(webdriver:'Chrome'(), Chrome).
  767%
  768%   By default, the imported module  is   associated  to an atom created
  769%   from the last segment of the dotted   name. Below we use an explicit
  770%   name.
  771%
  772%       ?- py_import('selenium.webdriver', [as(browser)]).
  773%       ?- py_call(browser:'Chrome'(), Chrome).
  774%
  775%   @error  permission_error(import_as,  py_module,  As)   if  there  is
  776%   already a module associated with As.
  777
  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]).
  787
  788%!  py_module(+Module:atom, +Source:string) is det.
  789%
  790%   Load Source into the Python module Module.   This  is intended to be
  791%   used together with the `string` _quasi quotation_ that supports long
  792%   strings in SWI-Prolog.   For example:
  793%
  794%   ```
  795%   :- use_module(library(strings)).
  796%   :- py_module(hello,
  797%                {|string||
  798%                 | def say_hello_to(s):
  799%                 |     print(f"hello {s}")
  800%                 |}).
  801%   ```
  802%
  803%   Calling this predicate multiple  times  with   the  same  Module and
  804%   Source is a no-op. Called with  a   different  source  creates a new
  805%   Python module that replaces the old in the global namespace.
  806%
  807%   @error python_error(Type, Data) is raised if Python raises an error.
  808
  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').
  902
  903%!  py_initialize(+Program, +Argv, +Options) is det.
  904%
  905%   Initialize  and configure  the  embedded Python  system.  If  this
  906%   predicate is  not called before any  other call to Python  such as
  907%   py_call/2, it is called _lazily_, passing the Prolog executable as
  908%   Program, passing Argv from the  Prolog flag `py_argv` and an empty
  909%   Options list.
  910%
  911%   Calling this predicate while the  Python is already initialized is
  912%   a  no-op.  This  predicate is  thread-safe, where  the first  call
  913%   initializes Python.
  914%
  915%   In addition to initializing the Python system, it
  916%
  917%     - Adds the directory holding `janus.py` to the Python module
  918%       search path.
  919%     - If Prolog I/O is not connected to the file handles 0,1,2,
  920%       it rebinds Python I/O to use the Prolog I/O.
  921%
  922%   @arg Options is currently ignored.  It will be used to provide
  923%   additional configuration options.
  924
  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    ).
  936
  937%!  py_connect_io is det.
  938%
  939%   If SWI-Prolog console streams are bound to something non-standard,
  940%   bind the Python console I/O to our streans.
  941
  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		 *******************************/
  958
  959%!  py_lib_dirs(-Dirs) is det.
  960%
  961%   True when Dirs is a list of directories searched for Python modules.
  962%   The elements of Dirs are in Prolog canonical notation.
  963%
  964%   @compat PIP
  965
  966py_lib_dirs(Dirs) :-
  967    py_call(sys:path, Dirs0),
  968    maplist(prolog_to_os_filename, Dirs, Dirs0).
  969
  970%!  py_add_lib_dir(+Dir) is det.
  971%!  py_add_lib_dir(+Dir, +Where) is det.
  972%
  973%   Add a directory to the Python  module   search  path.  In the second
  974%   form, Where is one of `first`   or `last`. py_add_lib_dir/1 adds the
  975%   directory as `last`. The property `sys:path`   is not modified if it
  976%   already contains Dir.
  977%
  978%   Dir is in Prolog notation. The added   directory  is converted to an
  979%   absolute path using the OS notation using prolog_to_os_filename/2.
  980%
  981%   If Dir is a _relative_ path, it   is taken relative to Prolog source
  982%   file when used as a _directive_ and  relative to the process working
  983%   directory when called as a predicate.
  984%
  985%   @compat PIP. Note  that  SWI-Prolog   uses  POSIX  file  conventions
  986%   internally, mapping to OS  conventions   inside  the predicates that
  987%   deal with files or explicitly   using prolog_to_os_filename/2. Other
  988%   systems may use the native file conventions in Prolog.
  989
  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`'-[] ]