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