Nested Python Modules

Hello,

I am trying to execute the following code:

py_call(selenium:webdriver:'Chrome'(), Browser).

But I get the following error:

ERROR:   module 'selenium' has no attribute 'webdriver'

I want to mimic the following Python code:

from selenium import webdriver

browser = webdriver.Chrome()

But Janus does not seem to support access to nested Python modules.

Any help is appreciated,
Quenio

Good question. I still do not fully understand what is going on. I can work around it using this code:

:- use_module(library(janus)).
:- use_module(library(strings)).

:- py_module(selenium,
             {|string||
              | from selenium import webdriver
              |}).

chrome(Chrome) :-
    py_call(selenium:webdriver:'Chrome'(), Chrome).

If some Python person knows what really happens and how to load a module from a package (as I think that is actually what happens) through the C API, please share.

I tried that, but I probably made some other mistake and misinterpreted the error :frowning: This works:

?- py_call('selenium.webdriver':'Chrome'(), Chrome).
Chrome = <py_WebDriver>(0x7ff30e9cc2e0).

I don’t really like this. Possibly we should have a py_import/1, so we can write

?- py_import(selenium:webdriver).
?- py_call(webdriver:'Chrome'(), Chrome)?

Opinions?

2 Likes

Ok, Added py_import/2. Copying the docs. Note this is tentative. As we try to keep the Janus interface compatible with XSB this may change if the XSB thinks this predicate is a good idea, but wants the defaults different. It may be dropped in case it is proven redundant (but I don’t think it is).

%!  py_import(+Spec, +Options) is det.
%
%   Import a Python module.  Janus   imports  modules automatically when
%   referred in py_call/2 and  related   predicates.  Importing a module
%   implies  the  module  is  loaded   using  Python's  ``__import__()``
%   built-in and added to a table  that   maps  Prolog atoms to imported
%   modules. This predicate explicitly imports a module and allows it to
%   be associated with a different  name.   This  is  useful for loading
%   _nested modules_, i.e., a specific module   from a Python package as
%   well as for  avoiding  conflicts.  For   example,  with  the  Python
%   `selenium` package installed, we can do in Python:
%
%       >>> from selenium import webdriver
%       >>> browser = webdriver.Chrome()
%
%   Without this predicate, we can do
%
%       ?- py_call('selenium.webdriver':'Chrome'(), Chrome).
%
%   For a single call this is  fine,   but  for making multiple calls it
%   gets cumbersome.  With this predicate we can write this.
%
%       ?- py_import('selenium.webdriver', []).
%       ?- py_call(webdriver:'Chrome'(), Chrome).
%
%   By default, the imported module  is   associated  to an atom created
%   from the last segment of the dotted   name. Below we use an explicit
%   name.
%
%       ?- py_import('selenium.webdriver', [as(browser)]).
%       ?- py_call(browser:'Chrome'(), Chrome).
%
%   @error  permission_error(import_as,  py_module,  As)   if  there  is
%   already a module associated with As.
4 Likes