A bundled Python interface

My message got cut off for some reason. I was saying maybe setting the prompts (ps1, ps2) to empty strings would give the desired behavior.

sys.ps1 and sys.ps2 are documented at
https://docs.python.org/3.10/library/sys.html
James

Thanks. I want the thing the other way around though. If I have Python embedded into Prolog and Prolog’s I/O redefined to use some window rather than file handles 0,1,2, I want Python’s I/O to use Prolog’s. Well, that now works (be it a bit ugly to my taste) And yes, Windows doesn’t get Unicode copy/paste > 0xffff to work very well. Works fine in Linux and MacOS …

That would improve things a bit, but then we have no prompt left :frowning: I now implemented this:

  • Use the audit event to see what the prompt is. Set a flag that we will be prompted.
  • Now, if the write handler sees this flag and the string written is the prompt, it simply ignores it :slight_smile:
  • Pass the prompt to the readline() handler.

That works flawlessly. The only small issue is that the Prolog command line history now captures both the Prolog and the Python commands if you use py_shell/0. Well, that also has it charm. This write hack smells a little fragile, but it surely is a step ahead. Tested in the Qt based swipl-win console. We automatically install this hack if Prolog I/O is not connected to the file handles 0,1,2. If Prolog uses 0,1,2, we do nothing.

Sync with XSB: ready to roll

We reached agreement on the Janus API in a Zoom call with the XSB developers last Friday. I have pushed changes that implement this. Summary

  • Python True, False and None now map to @true, @false and @none, where @ is a prefix operator. The notable advantage is that atoms are mapped unambiguously to Pythons strings in both directions.
  • Python tuples now map to -(…). This notably maps the binary tuple to the Prolog pair (key-value).
  • Dicts may be written as {k:v, ....}, py({k:v, ...}) or as a SWI-Prolog dict. With a common API to access dicts in library(janus), this allows writing portable code that passes dicts to Python and handles dicts returned from Python in Prolog.
  • Added py_is_object/1 as semidet predicate to check for Python object references.
  • Renamed add_py_lib_dir/1,2 to py_add_lib_dir/1,2
  • Changed the name of the Python package to janus-swi and its module to janus_swipl. This notably allows multiple versions for the various Prolog systems to coexist and avoids a conflict with the existing janus Python package.

I think that with these changes the API is very close to stable. From now on we won’t make any changes that are likely to break compatibility and we will implement mechanisms to minimize the impact of incompatible changes.

The version currently on GIT has many fixes to Python object reference count handling. The library now passes testing using AddressSanitizer.

To make life really easy for the Python programmer, it would be great to be able to do

 pip install swipl

to get a properly functioning version of SWI-Prolog. Does anyone know how to deal with installing large complex systems with many dependencies using pip? Can pip deal with cmake projects?

1 Like

Hi All

I just want to say that v 1 of the Janus interface proved very powerful to many of us in leveraging applications. We’ve used it to access to vector stores, SpaCy, RDF, Elasticsearch, Wikidata and many other functionality with very little, if any, interface code per se. V. 2.0 which Jan has described on this message board is even more powerful and easy to use. I can’t wait to see all the cool things that SWI developers come up with!

Theresa Swift

3 Likes

Installing on ubuntu 20.04 went without a hitch.
Installed to ~/sys using /usr/bin/python3.8.
Installed janus from git. This also installed OK.

Couple of gotchas with using janus though.

import janus
Traceback (most recent call last):
File “”, line 1, in
ModuleNotFoundError: No module named ‘janus’

However,

import janus_swi

Worked fine.

janus_swi.once(“Y is X+1”, {“X”:1})
{‘status’: True, ‘Y’: 2}

Looking good so far.

janus_swi.once(“assertz(a(1))”)
{‘status’: True}
janus_swi.once(“assertz(a(2))”)
{‘status’: True}

Still good but then:

janus_swi.once(“forall(a(X), writeln(X))”)
1
2
Traceback (most recent call last):
File “”, line 1, in
File “/home/eddie/.local/lib/python3.8/site-packages/janus_swi/janus.py”, line 130, in once
return swipl.call(query, inputs, keep)

I realised eventually I need to use _X rather than X. Obviously I need to understand the bindings malarky a bit better.

In general though, whenever there is an error that

NameError: name ‘janus’ is not defined

message pops up.

It is definitely finding the right swipl binary.
Doing import janus_swi as janus doesn’t change anything.

The features I’ve tested, once(), consult(), prolog() all work as expected it’s just the error messages that seem to get lost. Looking good though.

Hope to create a multi-threaded Python test app that calls out to Prolog soon.
(Edit: fixed output of forall() example).

1 Like

Thanks for the evaluation!

That is how it should work. When embedding SWI-Prolog into Python the one and only module is janus_swi, so we can have janus_xyz_prolog without conflicts. When Python is embedded into Prolog, both the modules janus and janus_swi are accessible as aliases. This all has pros and cons. Please suggest something better. Multiple Prolog systems implementing janus must be able to co-exist.

Thanks. There was an explicit reference to janus that should have pointed at janus_swi. Pushed a fix. And, indeed you need _X as otherwise the query wants to succeed binding X to a variable, which is not supported.

Looking forward. Things that can be added to the test suite are particularly welcome :slight_smile:

That is how it should work. When embedding SWI-Prolog into Python the one and only module is janus_swi, so we can have janus_xyz_prolog without conflicts. When Python is embedded into Prolog, both the modules janus and janus_swi are accessible as aliases. This all has pros and cons. Please suggest something better. Multiple Prolog systems implementing janus must be able to co-exist.

Calling it janus_swi seems OK to me. I was just blindly copying the “from janus import *” from SWI-Prolog -- Calling Prolog from Python, possibly I’m confused about the context of that page.

You can always do import janus as my_janus.

This is considered to be bad style for large code bases (even with fancy IDEs because it can be difficult to statically resolve names due to Python’s dynamic nature); better to do import janus and explicitly give the module name janus when referencing imported classes or functions.

The Google style guide is based on years of experience and has reviewed by very good Python programmers, including some “core developers” - here’s a link to the pros and cons of the various import options:

I was just blindly copying the “from janus import *”

This is considered to be bad style for large code bases (even with fancy IDEs because it can be difficult to statically resolve names due to Python’s dynamic nature); better to do import janus and explicitly give the module name janus when referencing imported classes or functions.

For sure, I was meaning just the name janus vs janus_swi, I could have made that clearer!

That doesn’t solve the problem. We’ll be faced with the situation where both XSB and SWI-Prolog have an implementation of the Janus “standard” :slight_smile: , but the implementation is independent. From the Python side, we now assume a package per Prolog system that is called janus_*, where the * identifies the Prolog system. If we do so, I can do

 pip install janus_swi
 pip insall janus_xsb
 ...

And next

import janus_swi as janus

and simply replace janus_swi with janus_xsb if I want to run the same code with XSB. Or not alias at all and chat to both Prolog systems from Python :slight_smile:

Yes. The website is still documenting the situation before the changes that followed from the discussion with the XSB team. Will re-release shortly to get this all in sync.

marco@MacBookoBachini ~ % python
Python 3.11.4 (main, Jun 10 2023, 10:31:47) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from janus_swi import *
>>> once("assertz(dog(fido))")
{'status': True}
>>> once("assertz(dog(elvis))")
{'status': True}
>>> once("assertz(cat(susie))")
{'status': True}
>>> once("assertz(cat(arturo))")
{'status': True}
>>> [a["Dog"] for a in Query("dog(Dog)")]
['fido', 'elvis']
>>> [a["Cat"] for a in Query("cat(Cat)")]
['susie', 'arturo']
>>> once("assertz(animal(_X) :- dog(_X))")
{'status': True}
>>> once("assertz(animal(_X) :- cat(_X))")
{'status': True}
>>> [a["Animal"] for a in Query("animal(Animal)")]
['fido', 'elvis', 'susie', 'arturo']
>>> once("retract(dog(fido))")
{'status': True}
>>> [a["Animal"] for a in Query("animal(Animal)")]
['elvis', 'susie', 'arturo']

Following the site’s suggestions I have played a little bit with it today and it works :+1: writing the import directive in that way seemed the only way (as far as I could see, but maybe there are other ways…) for not having to prefix janus_swi each time

Great. You should not include data into your query string though. Only the “structure”, so you get

>>> once("assertz(dog(Dog))",  {"Dog":"fido"})

Ok, for a test from the terminal your thing is ok of course, but if you do that with generated strings for the dogs you’ll build up a large cache of queries and you’ll get problems with dogs with complicated names with spaces, quotes, etc.

You can either import the specific function or import the module using an alias. The docs suggest

import janus_swi as janus

That will allow you to use the same program with XSB, simply by replacing janus_swi with janus_xsb.

1 Like

Small detail - the build fails for -DCMAKE_BUILD_TYPE=Debug (and probably for other build types):

-- Configuring SWI-Prolog-9.1.14
Trying to find debug version of Python
-- Could NOT find Python (missing: Python_EXECUTABLE Python_INCLUDE_DIRS Python_LIBRARIES Interpreter Development Development.Module Development.Embed) (Required is at least version "3.6")
    Reason given by package: 
        Interpreter: Wrong ABI for the interpreter "/home/peter/.local/bin/python3"

You have to install the debug version of Python as well. Probably it should fall back to the nodebug version if it cannot find the debug version. Well, the debug build type is for developers only.