mcocdawc
mcocdawc

Reputation: 1867

Properly re-expose submodule (or is this a bug in pylance)

I am working on a python package chemcoord with several subpackages, some of whom should be exposed to the root namespace.

The repository is here, the relevant __init__.py file is here.

For example there is a chemcoord.cartesian_coordinates.xyz_functions that should be accessible as chemcoord.xyz_functions

Accessible, in particular, means that the user should be able to write:

from chemcoord.xyz_functions import allclose

If I write in my __init__.py

import chemcoord.cartesian_coordinates.xyz_functions as xyz_functions

then I can use chemcoord.xyz_functions in the code, but I cannot do

from chemcoord.xyz_functions import allclose

If I do the additional ugly/hacky (?) trick of modifying sys.modules in the __init__.py as in

import sys
sys.modules["chemcoord.xyz_functions"] = xyz_functions

then I can write

from chemcoord.xyz_functions import allclose

But it feels ugly and hacky.

Recently I got warnings from PyLance about

Import "chemcoord.xyz_functions" could not be resolved

Which leads to my two questions:

  1. Is my approach of reexposing the submodule correct, or is there a cleaner way?
  2. If the answer to question 1 is solved and I still get warnings from PyLance, is there a bug in PyLance?

Upvotes: 1

Views: 62

Answers (1)

J_H
J_H

Reputation: 20550

nit: # -*- coding: utf-8 -*- hasn't been needed in python source files for a very very long time, given that it is default starting with interpreter 3.0. And 3.8 went EOL last year, so really you only need to worry about 3.9 and later.

And these seem odd:
... import Cartesian as Cartesian,
... import Zmat as Zmat

sys.modules

I completely agree with you that the sys.modules["chemcoord.xyz_functions"] assignment is not appropriate. And I agree that apps consuming the Public API should be able to use a short from chemcoord.xyz_functions import allclose, dot.

Organizing everything under src/ is very nice and I thank you for that. The entire cartesian_coordinates/ folder keeps things tidy for the package developer and makes good sense. However, you might possibly wish to "hide" a code module by renaming it to _xyz_functions.py, with leading _ underscore. (I don't know what promises you've made to app developers in v2.1.2 and previous, which might require you to still expose some or all of that.)

new module

Focusing on the OP question, you complain that an app author currently cannot do

from chemcoord.xyz_functions import allclose

Honestly, it's very simple. You're just a little hung up on that
src/chemcoord/cartesian_coordinates/xyz_functions.py filename. You're thinking that is the "right" location, but it's just a private implementation detail, separate from the Public API you choose to expose, which plays into why we might want to hide it. I claim that what you want to do, to answer the original question, is instead of editing an __init__.py module, you want to create a new src/chemcoord/xyz_functions.py module. It can pull in allclose(), dot(), etc., and make them visible with conveniently short names for app authors.


  1. Yes, there's a cleaner way, just define a new public module.
  2. No, there's no trouble with pylance here. (Also, you might prefer to run $ pyright . .)

Upvotes: 2

Related Questions