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, 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_free/1, % +Obj 44 py_is_object/1, % @Term 45 py_with_gil/1, % :Goal 46 47 py_func/3, % +Module, +Func, -Return 48 py_func/4, % +Module, +Func, -Return, +Options 49 py_dot/4, % +Module, +ObjRef, +Meth, ?Ret 50 py_dot/5, % +Module, +ObjRef, +Meth, -Ret, +Options 51 52 values/3, % +Dict, +Path, ?Val 53 keys/2, % +Dict, ?Keys 54 key/2, % +Dict, ?Key 55 items/2, % +Dict, ?Items 56 57 py_shell/0, 58 59 py_pp/1, % +Term 60 py_pp/2, % +Stream, +Term 61 py_pp/3, % +Stream, +Term, +Options 62 63 py_obj_dir/2, % +ObjRef,-List 64 py_obj_dict/2, % obj_dict(+ObjRef, -Dict) 65 66 py_initialize/3, % +Program, +Argv, +Options 67 py_lib_dirs/1, % -Dirs 68 py_add_lib_dir/1, % +Dir 69 py_add_lib_dir/2, % +Dir,+Where 70 71 op(200, fy, @), % @constant 72 op(50, fx, #) % #Value 73 ]). 74:- use_module(library(apply_macros), []). 75:- autoload(library(lists), [append/3, member/2]). 76:- autoload(library(apply), [maplist/2, exclude/3, maplist/3]). 77:- autoload(library(error), [must_be/2, domain_error/2]). 78:- autoload(library(dicts), [dict_keys/2]). 79:- autoload(library(option), [dict_options/2]). 80:- autoload(library(prolog_code), [comma_list/2]). 81:- autoload(library(readutil), [read_line_to_string/2]). 82:- autoload(library(wfs), [call_delays/2, delays_residual_program/2]). 83 84:- if(\+current_predicate(py_call/1)). 85:- if(current_prolog_flag(windows, true)). 86:- use_module(library(shlib), [win_add_dll_directory/1]). 87 88% Just having the Python dir in PATH seems insufficient. We also need to 89% add the directory to the DLL search path. 90add_python_dll_dir :- 91 absolute_file_name(path('python3.dll'), DLL, [access(read)]), 92 file_directory_name(DLL, Dir), 93 win_add_dll_directory(Dir). 94:- initialization(add_python_dll_dir, now). 95:- endif. 96 97:- use_foreign_library(foreign(janus), [visibility(global)]). 98:- endif. 99 100:- meta_predicate py_with_gil(). 101 102:- predicate_options(py_call/3, 3, 103 [ py_object(boolean), 104 py_string_as(oneof([string,atom])) 105 ]). 106:- predicate_options(py_func/4, 4, 107 [ sizecheck(boolean), 108 pass_to(py_call/3, 3) 109 ]). 110:- predicate_options(py_dot/5, 5, 111 [ sizecheck(boolean), 112 pass_to(py_call/3, 3) 113 ]). 114 115:- public 116 py_initialize/0, 117 py_call_string/3, 118 py_write/2, 119 py_readline/4.
sys.version. If a Python virtual environment (venv) is
active, indicate this with the location of this environment found.
141py_version :-
142 py_call(sys:version, X),
143 print_message(information, janus(version(X))),
144 ( py_venv(VEnvDir, EnvSiteDir)
145 -> print_message(information, janus(venv(VEnvDir, EnvSiteDir)))
146 ; true
147 ).
Arguments to Python functions use the Python conventions. Both
positional and keyword arguments are supported. Keyword
arguments are written as Name = Value and must appear after the
positional arguments.
Below are some examples.
% call a built-in
?- py_call(print("Hello World!\n")).
true.
% call a built-in (alternative)
?- py_call(builtins:print("Hello World!\n")).
true.
% call function in a module
?- py_call(sys:getsizeof([1,2,3]), Size).
Size = 80.
% call function on an attribute of a module
?- py_call(sys:path:append("/home/bob/janus")).
true
% get attribute from a module
?- py_call(sys:path, Path)
Path = ["dir1", "dir2", ...]
Given a class in a file dog.py such as the following example from
the Python documentation
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
We can interact with this class as below. Note that $Doc in the
SWI-Prolog toplevel refers to the last toplevel binding for the
variable Dog.
?- py_call(dog:'Dog'("Fido"), Dog).
Dog = <py_Dog>(0x7f095c9d02e0).
?- py_call($Dog:add_trick("roll_over")).
Dog = <py_Dog>(0x7f095c9d02e0).
?- py_call($Dog:tricks, Tricks).
Dog = <py_Dog>(0x7f095c9d02e0),
Tricks = ["roll_over"]
py_call/1 can also be used to set an attribute on a module or object
using the syntax py_call(Obj:Attr = Value). For example:
?- py_call(dog:'Dog'("Fido"), Dog),
py_call(Dog:owner = "Bob"),
py_call(Doc:owner, Owner).
Dog = <py_Dog>(0x7ffff7112170),
Owner = "Bob".
If the principal term of the first argument is not Target:Func,
The argument is evaluated as the initial target, i.e., it must be an
object reference or a module. For example:
?- py_call(dog:'Dog'("Fido"), Dog),
py_call(Dog, X).
Dog = X, X = <py_Dog>(0x7fa8cbd12050).
?- py_call(sys, S).
S = <py_module>(0x7fa8cd582390).
Options processed:
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.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 long, float, string or tuple. Instances of
sub classes of these base classes are controlled by this option.Obj:Attr = Value construct is not accepted.__iter__ on the result to get the iterator itself.__next__ function of the iterator.
The example below uses the built-in iterator range():
?- py_iter(range(1,3), X). X = 1 ; X = 2.
Note that the implementation performs a look ahead, i.e., after successful unification it calls `__next__()` again. On failure the Prolog predicate succeeds deterministically. On success, the next candidate is stored.
Note that a Python generator is a Python _iterator. Therefore,
given the Python generator expression below, we can use
py_iter(squares(1,5),X) to generate the squares on backtracking.
def squares(start, stop):
for i in range(start, stop):
yield i * i
eval, file (default) or single.existence_error. Note that by decrementing the reference
count, we make the reference invalid from Prolog. This may not
actually delete the object because the object may have references
inside Python.
Prolog references to Python objects are subject to atom garbage collection and thus normally do not need to be freed explicitly.
once(Goal) while holding the Phyton GIL (Global
Interpreter Lock). Note that py_call/1,2 also locks 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.327 /******************************* 328 * COMPATIBILIY * 329 *******************************/
342py_func(Module, Function, Return) :- 343 py_call(Module:Function, Return). 344py_func(Module, Function, Return, Options) :- 345 py_call(Module:Function, Return, Options).
354py_dot(_Module, ObjRef, MethAttr, Ret) :- 355 py_call(ObjRef:MethAttr, Ret). 356py_dot(_Module, ObjRef, MethAttr, Ret, Options) :- 357 py_call(ObjRef:MethAttr, Ret, Options). 358 359 360 /******************************* 361 * PORTABLE ACCESS TO DICTS * 362 *******************************/
369values(Dict, Key, Val), is_dict(Dict), atom(Key) => 370 get_dict(Key, Dict, Val). 371values(Dict, Keys, Val), is_dict(Dict), is_list(Keys) => 372 get_dict_path(Keys, Dict, Val). 373values(py({CommaDict}), Key, Val) => 374 comma_values(CommaDict, Key, Val). 375values({CommaDict}, Key, Val) => 376 comma_values(CommaDict, Key, Val). 377 378get_dict_path([], Val, Val). 379get_dict_path([H|T], Dict, Val) :- 380 get_dict(H, Dict, Val0), 381 get_dict_path(T, Val0, Val). 382 383comma_values(CommaDict, Key, Val), atom(Key) => 384 comma_value(Key, CommaDict, Val). 385comma_values(CommaDict, Keys, Val), is_list(Keys) => 386 comma_value_path(Keys, CommaDict, Val). 387 388comma_value(Key, Key:Val0, Val) => 389 Val = Val0. 390comma_value(Key, (_,Tail), Val) => 391 comma_value(Key, Tail, Val). 392 393comma_value_path([], Val, Val). 394comma_value_path([H|T], Dict, Val) :- 395 comma_value(H, Dict, Val0), 396 comma_value_path(T, Val0, Val).
402keys(Dict, Keys), is_dict(Dict) => 403 dict_keys(Dict, Keys). 404keys(py({CommaDict}), Keys) => 405 comma_dict_keys(CommaDict, Keys). 406keys({CommaDict}, Keys) => 407 comma_dict_keys(CommaDict, Keys). 408 409comma_dict_keys((Key:_,T), Keys) => 410 Keys = [Key|KT], 411 comma_dict_keys(T, KT). 412comma_dict_keys(Key:_, Keys) => 413 Keys = [Key].
420key(Dict, Key), is_dict(Dict) => 421 dict_pairs(Dict, _Tag, Pairs), 422 member(Key-_, Pairs). 423key(py({CommaDict}), Keys) => 424 comma_dict_key(CommaDict, Keys). 425key({CommaDict}, Keys) => 426 comma_dict_key(CommaDict, Keys). 427 428comma_dict_key((Key:_,_), Key). 429comma_dict_key((_,T), Key) :- 430 comma_dict_key(T, Key).
436items(Dict, Items), is_dict(Dict) => 437 dict_pairs(Dict, _, Pairs), 438 maplist(pair_item, Pairs, Items). 439items(py({CommaDict}), Keys) => 440 comma_dict_items(CommaDict, Keys). 441items({CommaDict}, Keys) => 442 comma_dict_items(CommaDict, Keys). 443 444pair_item(K-V, K:V). 445 446comma_dict_items((Key:Value,T), Keys) => 447 Keys = [Key:Value|KT], 448 comma_dict_items(T, KT). 449comma_dict_items(Key:Value, Keys) => 450 Keys = [Key:Value]. 451 452 453 /******************************* 454 * SHELL * 455 *******************************/
janus as below.
from janus import *
So, we can do
?- py_shell.
...
>>> 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.
481py_shell :- 482 import_janus, 483 py_call(janus_swi:interact(), _). 484 485import_janus :- 486 py_call(sys:hexversion, V), 487 V >= 0x030A0000, % >= 3.10 488 !, 489 py_run("from janus_swi import *", py{}, py{}, _, []). 490import_janus :- 491 print_message(warning, janus(py_shell(no_janus))). 492 493 494 /******************************* 495 * UTILITIES * 496 *******************************/
pformat() from the Python module
pprint to do the actual formatting. Options is translated into
keyword arguments passed to pprint.pformat(). 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}
513py_pp(Term) :- 514 py_pp(current_output, Term, []). 515 516py_pp(Term, Options) :- 517 py_pp(current_output, Term, Options). 518 519py_pp(Stream, Term, Options) :- 520 opts_kws(Options, Kws), 521 PFormat =.. [pformat, Term|Kws], 522 py_call(pprint:PFormat, String), 523 write(Stream, String). 524 525opts_kws(Options, Kws) :- 526 dict_options(Dict, Options), 527 dict_pairs(Dict, _, Pairs), 528 maplist(pair_kws, Pairs, Kws). 529 530pair_kws(Name-Value, Name=Value).
540py_obj_dir(ObjRef, List) :- 541 py_call(ObjRef:'__dir__'(), List). 542 543py_obj_dict(ObjRef, Dict) :- 544 py_call(ObjRef:'__dict__', Dict). 545 546 547 /******************************* 548 * INIT * 549 *******************************/ 550 551:- dynamic py_venv/2 as volatile. 552 553% py_initialize is det. 554% 555% Used as a callback from C for lazy initialization of Python. 556 557py_initialize :- 558 getenv('VIRTUAL_ENV', VEnv), 559 prolog_to_os_filename(VEnvDir, VEnv), 560 atom_concat(VEnvDir, '/pyvenv.cfg', Cfg), 561 access_file(Cfg, read), 562 !, 563 current_prolog_flag(executable, Program), 564 py_initialize(Program, ['-I'], []), 565 py_call(sys:prefix = VEnv), 566 venv_update_path(VEnvDir). 567py_initialize :- 568 current_prolog_flag(executable, Program), 569 current_prolog_flag(argv, Argv), 570 py_initialize(Program, Argv, []). 571 572venv_update_path(VEnvDir) :- 573 py_call(sys:version_info, Info), % Tuple 574 Info =.. [_,Major,Minor|_], 575 format(string(EnvSiteDir), 576 '~w/lib/python~w.~w/site-packages', 577 [VEnvDir, Major, Minor]), 578 prolog_to_os_filename(EnvSiteDir, PyEnvSiteDir), 579 ( exists_directory(EnvSiteDir) 580 -> true 581 ; print_message(warning, 582 janus(venv(no_site_package_dir(VEnvDir, EnvSiteDir)))) 583 ), 584 py_call(sys:path, Path0), 585 exclude(is_site_dir, Path0, Path1), 586 append(Path1, [PyEnvSiteDir], Path), 587 py_call(sys:path = Path), 588 print_message(silent, janus(venv(VEnvDir, EnvSiteDir))), 589 asserta(py_venv(VEnvDir, EnvSiteDir)). 590 591is_site_dir(OsDir) :- 592 prolog_to_os_filename(PlDir, OsDir), 593 file_base_name(PlDir, Dir0), 594 downcase_atom(Dir0, Dir), 595 no_env_dir(Dir). 596 597no_env_dir('site-packages'). 598no_env_dir('dist-packages').
Calling this predicate while the Python is already initialized is a no-op. This predicate is thread-safe, where the first thread initializes Python.
In addition to initializing the Python system, it
janus.py to the Python module
search path.
622py_initialize(Program, Argv, Options) :-
623 ( py_initialize_(Program, Argv, Options)
624 -> absolute_file_name(library('python/janus.py'), Janus,
625 [ access(read) ]),
626 file_directory_name(Janus, PythonDir),
627 py_add_lib_dir(PythonDir, first),
628 py_connect_io
629 ; true
630 ).637py_connect_io :- 638 maplist(non_file_stream, 639 [0-user_input, 1-user_output, 2-user_error], 640 NonFiles), 641 Call =.. [connect_io|NonFiles], 642 py_call(janus_swi:Call). 643 644non_file_stream(Expect-Stream, Bool) :- 645 ( stream_property(Stream, file_no(Expect)) 646 -> Bool = @false 647 ; Bool = @true 648 ). 649 650 /******************************* 651 * PATHS * 652 *******************************/
659py_lib_dirs(Dirs) :-
660 py_call(sys:path, Dirs0),
661 maplist(prolog_to_os_filename, Dirs, Dirs0).first or last. py_add_lib_dir/1 adds the
directory as last.
Dir is in Prolog notation. The added directory is converted to an absolute path using the OS notation.
A flexible way to add the directory holding the current Prolog file to the Python search path is in the template below. The here/0 predicate can be replaced by any predicate defined in the file, either above or below the initializing/1 directive. A simple name like here/0 is good style when this code is part of a Prolog module.
here.
:- initialization
( source_file(here, File),
file_directory_name(File, Dir),
py_add_lib_dir(Dir, first)
).
688py_add_lib_dir(Dir) :- 689 py_add_lib_dir(Dir, last). 690 691py_add_lib_dir(Dir, Where) :- 692 absolute_file_name(Dir, AbsDir), 693 prolog_to_os_filename(AbsDir, OSDir), 694 ( Where == last 695 -> py_call(sys:path:append(OSDir), _) 696 ; Where == first 697 -> py_call(sys:path:insert(0, OSDir), _) 698 ; must_be(oneof([first,last]), Where) 699 ). 700 701 702 /******************************* 703 * CALLBACK * 704 *******************************/ 705 706:- dynamic py_call_cache/8 as volatile. 707 708:- meta_predicate py_call_string(, , ). 709 710% py_call_string(:String, +DictIn, -Dict) is nondet. 711% 712% Support janus.once() and janus.Query(). Parses String into a goal 713% term. Next, all variables from the goal term that appear in DictIn 714% are bound to the value from this dict. Dict is created from the 715% remaining variables, unless they start with an underscore (e.g., 716% `_Time`) and the key `truth. On success, the Dict values contain 717% the bindings from the answer and `truth` is either `true` or 718% `Undefined`. On failure, the Dict values are bound to `None` and the 719% `truth` is `false`. 720% 721% Parsing and distributing the variables over the two dicts is cached. 722 723py_call_string(M:String, Input, Dict) :- 724 py_call_cache(String, Input, TV, M, Goal, Dict, Truth, OutVars), 725 !, 726 py_call(TV, M:Goal, Truth, OutVars). 727py_call_string(M:String, Input, Dict) :- 728 term_string(Goal, String, [variable_names(Map)]), 729 unbind_dict(Input, VInput), 730 exclude(not_in_projection(VInput), Map, OutBindings), 731 dict_create(Dict, bindings, [truth=Truth|OutBindings]), 732 maplist(arg(2), OutBindings, OutVars), 733 TV = Input.get(truth, 'PLAIN_TRUTHVALS'), 734 asserta(py_call_cache(String, VInput, TV, M, Goal, Dict, Truth, OutVars)), 735 VInput = Input, 736 py_call(TV, M:Goal, Truth, OutVars). 737 738py_call('NO_TRUTHVALS', M:Goal, Truth, OutVars) => 739 ( call(M:Goal) 740 *-> bind_status_no_no_truthvals(Truth) 741 ; Truth = @false, 742 maplist(bind_none, OutVars) 743 ). 744py_call('PLAIN_TRUTHVALS', M:Goal, Truth, OutVars) => 745 ( call(M:Goal) 746 *-> bind_status_plain_truthvals(Truth) 747 ; Truth = @false, 748 maplist(bind_none, OutVars) 749 ). 750py_call('DELAY_LISTS', M:Goal, Truth, OutVars) => 751 ( call_delays(M:Goal, Delays) 752 *-> bind_status_delay_lists(Delays, Truth) 753 ; Truth = @false, 754 maplist(bind_none, OutVars) 755 ). 756py_call('RESIDUAL_PROGRAM', M:Goal, Truth, OutVars) => 757 ( call_delays(M:Goal, Delays) 758 *-> bind_status_residual_program(Delays, Truth) 759 ; Truth = @false, 760 maplist(bind_none, OutVars) 761 ). 762 763not_in_projection(Input, Name=Value) :- 764 ( get_dict(Name, Input, Value) 765 -> true 766 ; sub_atom(Name, 0, _, _, '_') 767 ). 768 769bind_none(@none). 770 771bind_status_no_no_truthvals(@true). 772 773bind_status_plain_truthvals(Truth) => 774 ( '$tbl_delay_list'([]) 775 -> Truth = @true 776 ; py_undefined(Truth) 777 ). 778 779bind_status_delay_lists(true, Truth) => 780 Truth = @true. 781bind_status_delay_lists(Delays, Truth) => 782 py_call(janus:'Undefined'(prolog(Delays)), Truth). 783 784bind_status_residual_program(true, Truth) => 785 Truth = @true. 786bind_status_residual_program(Delays, Truth) => 787 delays_residual_program(Delays, Program), 788 py_call(janus:'Undefined'(prolog(Program)), Truth). 789 790py_undefined(X) :- 791 py_call(janus:undefined, X). 792 793unbind_dict(Dict0, Dict) :- 794 dict_pairs(Dict0, Tag, Pairs0), 795 maplist(unbind, Pairs0, Pairs), 796 dict_pairs(Dict, Tag, Pairs). 797 798unbind(Name-_, Name-_) :- 799 sub_atom(Name, 0, 1, _, Char1), 800 char_type(Char1, prolog_var_start), 801 !. 802unbind(NonVar, NonVar). 803 804 805 /******************************* 806 * SUPPORT PYTHON CALLS * 807 *******************************/ 808 809:- public 810 px_cmd/3, 811 px_qdet/4, 812 px_comp/7. 813 814% These predicates are helpers for the corresponding Python functions 815% in janus.py. 816 817px_cmd(M, P, Tuple) :- 818 atom_string(Pred, P), 819 atom_string(Module, M), 820 ( compound(Tuple) 821 -> compound_name_arguments(Tuple, _, Args), 822 Goal =.. [Pred|Args] 823 ; Goal = Pred 824 ), 825 call(Module:Goal). 826 827px_qdet(M, P, Tuple, Ret) :- 828 atom_string(Pred, P), 829 atom_string(Module, M), 830 ( compound(Tuple) 831 -> compound_name_arguments(Tuple, _, Args), 832 append(Args, [Ret], GArgs), 833 Goal =.. [Pred|GArgs], 834 call(Module:Goal) 835 ; call(Module:Pred, Ret) 836 ). 837 838px_comp(M, P, Tuple, Vars, Set, TV, Ret) :- 839 atom_string(Pred, P), 840 atom_string(Module, M), 841 length(Out, Vars), 842 ( compound(Tuple) 843 -> compound_name_arguments(Tuple, _, Args), 844 append(Args, Out, GArgs), 845 Goal =.. [Pred|GArgs] 846 ; Goal =.. [Pred|Out] 847 ), 848 compound_name_arguments(OTempl0, -, Out), 849 tv_goal_and_template(TV, Module:Goal, FGoal, OTempl0, OTempl), 850 findall(OTempl, FGoal, Ret0), 851 ( Set == @true 852 -> sort(Ret0, Ret) 853 ; Ret = Ret0 854 ). 855 856:- meta_predicate 857 call_delays_py(, ). 858 859% 0,1,2: TruthVal(Enum) from janus.py 860tv_goal_and_template('NO_TRUTHVALS', 861 Goal, Goal, Templ, Templ) :- !. 862tv_goal_and_template('PLAIN_TRUTHVALS', 863 Goal, ucall(Goal, TV), Templ, -(Templ,TV)) :- !. 864tv_goal_and_template('DELAY_LISTS', 865 Goal, call_delays_py(Goal, TV), Templ, -(Templ,TV)) :- !. 866tv_goal_and_template(Mode, _, _, _, _) :- 867 domain_error("px_comp() truth", Mode). 868 869:- public 870 ucall/2, 871 call_delays_py/2. 872 873ucall(Goal, TV) :- 874 call(Goal), 875 ( '$tbl_delay_list'([]) 876 -> TV = 1 877 ; TV = 2 878 ). 879 880call_delays_py(Goal, PyDelays) :- 881 call_delays(Goal, Delays), 882 ( Delays == true 883 -> PyDelays = [] 884 ; comma_list(Delays, Array), 885 maplist(term_string, Array, PyDelays) 886 ). 887 888 889 /******************************* 890 * PYTHON I/O * 891 *******************************/ 892 893% py_write(+Stream, -String) is det. 894% py_readline(+Stream, +Size, +Prompt, +Line) is det. 895% 896% Called from redefined Python console I/O to write/read using the 897% Prolog streams. 898 899:- '$hide'((py_write/1, 900 py_readline/4)). 901 902py_write(Stream, String) :- 903 notrace(format(Stream, '~s', [String])). 904 905py_readline(Stream, Size, Prompt, Line) :- 906 notrace(py_readline_(Stream, Size, Prompt, Line)). 907 908py_readline_(Stream, _Size, Prompt, Line) :- 909 prompt1(Prompt), 910 read_line_to_string(Stream, Read), 911 ( Read == end_of_file 912 -> Line = "" 913 ; string_concat(Read, "\n", Line), 914 py_add_history(Read) 915 ). 916 917py_add_history(Line) :- 918 ignore(catch(prolog:history(user_input, add(Line)), _, true)). 919 920 921 /******************************* 922 * COMPILING * 923 *******************************/ 924 925% py_consult(+File, +Data, +Module) is det. 926% 927% Support janus.consult(file, data=None, module='user'). 928 929:- public py_consult/3. 930py_consult(File, @none, Module) => 931 consult(Module:File). 932py_consult(File, Data, Module) => 933 setup_call_cleanup( 934 open_string(Data, In), 935 load_files(Module:File, [stream(In)]), 936 close(In)). 937 938 939 /******************************* 940 * MESSAGES * 941 *******************************/ 942 943:- multifile 944 prolog:error_message//1, 945 prolog:message//1. 946 947prologerror_message(python_error(Class, Value, _Stack)) --> 948 { py_str(Value, Message) 949 }, 950 [ 'Python ', ansi(code, "'~w'", [Class]), ':', nl, 951 ' ~w'-[Message] 952 ]. 953 954prologmessage(janus(Msg)) --> 955 message(Msg). 956 957message(version(V)) --> 958 [ 'Janus embeds Python ~w'-[V] ]. 959message(venv(Dir, _EnvSiteDir)) --> 960 [ 'Janus: using venv from ~p'-[Dir] ]. 961message(venv(no_site_package_dir(VEnvDir, Dir))) --> 962 [ 'Janus: venv dirrectory ~p does not contain ~p'-[VEnvDir, Dir] ]. 963message(py_shell(no_janus)) --> 964 [ 'Janus: py_shell/0: Importing janus into the Python shell requires Python 3.10 or later.', nl, 965 'Run "', ansi(code, 'from janus import *', []), '" in the Python shell to import janus.' 966 ]
Call Python from Prolog
This library implements calling Python from Prolog. It is available directly from Prolog if the janus package is bundled, providing 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. */