# Nested Python Modules

**URL:** <https://swi-prolog.discourse.group/t/nested-python-modules/7330>\
**Category:** General\
**Created:** [March 24, 2024, 1:29am UTC](https://swi-prolog.discourse.group/t/nested-python-modules/7330 "2024-03-24T01:29:12Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![quenio](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/quenio/32/5073_2.png) [@quenio](https://swi-prolog.discourse.group/u/quenio)\
**Post date:** [March 24, 2024, 1:29am UTC](https://swi-prolog.discourse.group/t/nested-python-modules/7330/1 "2024-03-24T01:29:12Z")

</div>

Hello,

I am trying to execute the following code:

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

```

But I get the following error:

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

```

I want to mimic the following Python code:

```prolog
from selenium import webdriver

browser = webdriver.Chrome()

```

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

Any help is appreciated,  
Quenio

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2024, 9:43am UTC](https://swi-prolog.discourse.group/t/nested-python-modules/7330/2 "2024-03-24T09:43:44Z")

</div>

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

```prolog
:- 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.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2024, 1:06pm UTC](https://swi-prolog.discourse.group/t/nested-python-modules/7330/4 "2024-03-24T13:06:50Z")

</div>

> [@anon95304481](#):
>
> Replace the colons “:” by dots “.” in the module name

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

```prolog
?- 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

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

```

Opinions?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2024, 5:28pm UTC](https://swi-prolog.discourse.group/t/nested-python-modules/7330/6 "2024-03-24T17:28:58Z")

</div>

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).

```prolog
%! 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.

```
