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(). 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 992systemterm_expansion((:- py_add_lib_dir(Dir0)), Directive) :- 993 system:term_expansion((:- py_add_lib_dir(Dir0, last)), Directive). 994systemterm_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 1048prologrepl_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(, ). 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 1312prologerror_message(python_error(Class, Value)) --> 1313 { py_str(Value, Message) 1314 }, 1315 [ 'Python ', ansi(code, "'~w'", [Class]), ':', nl, 1316 ' ~w'-[Message] 1317 ]. 1318prologerror_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 1323prologmessage_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 1343prologmessage(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`'-[] ]