JE_Muc
JE_Muc

Reputation: 5784

Make only specific symbols in a module importable, f.i. with `__all__`

tl;dr:

I like clean APIs, thus I don't like having "internal" imports, like import numpy as np inside of my modules, show up in the module API.

In my package (installable with setup.py) I have several sub-modules, of which each imports a set of modules, f.i. numpy or builtins like sys.
Let's say in my package mytools I have a submodule mytools.mysubtools in which I define a function foo, which requires numpy (imported with import numpy as np). foo is added to __all__ = ['foo'] .
Now I import this submodule with import mytools.mysubtools as mst. When accessing mst with code-completition (for example with Spyder IDE, PyCharm, Kite...) etc., it always shows all symbols and imports in mst, in this case foo and np.
Can I avoid having imports like np show up in the imported module, so that only foo shows up?

detailed description

Let's say my package structure looks like this:

/mytools/
 |-- __init__.py  # "mytools-init"
 |-- my_subtools.py
 |-- some_other_subtools.py

The __init__.py file looks like this:

from . import my_subtools as mysubtools
__all__ = ['mysubtools']

And my_subtools.py:

import numpy as np

__all__ = ['foo']

def foo():
    return np.sqrt(4**3)

Now I import my_subtools with

import mytools.mysubtools as mst

When using the imported module, code completition etc. always shows np and foo, f.i. when typing mst.. For larger modules, with many imports and/or function/class definitions, this makes it really hard to use code completition, since a simple inspection of the API shows all imported modules and all definitions.

With __all__ it is possible to avoid importing all symbols for wild/star imports, like from mytools.mysubtools import *. Can __all__ somehow also be used to mangle explicit imports?

Or is there any recommended way to avoid importing all symbols of a module?
For functions, I use name mangling, f.i. def _baz(): return 4**4.
But is this also advisable for imports, f.i. import numpy as _np? Any downsides?

What I'm looking for:
An explicit solution, since: Explicit is better than implicit.
For example something like __all__, but for explicit imports. Or something like __except__ = ['np'] which excludes symbols from imports.

Upvotes: 0

Views: 569

Answers (2)

JE_Muc
JE_Muc

Reputation: 5784

Thanks to the comments of @chepner, I've been looking into the linked implementation in the python standard library.

The conclusion is, that name mangling of imported modules is used frequently in the standard library. This is in my opinion a quite clear sign, that this is considered pythonic and/or has no severe downsides.
Several examples of name-mangling imports can be found, f.i.:

Which modules are name-mangled seems to change from file to file. It seems that the more "system-relevant" the module is, the less likely it is to be name mangled. For example imports like

import re as _re
import math as _math

are frequently name mangled, while import os as _os is less commong. This is a merely my impression, so it may not be true in general.

Upvotes: 1

AKX
AKX

Reputation: 169308

__all__ = [...] is a good hint of the "exportable" names, but it's just a hint. (Some IDEs such as PyCharm do tend to get the clue though.)

I definitely wouldn't do import numpy as _np; you'd just be making your life harder.

You can't prevent someone from importing things from your module, if they're there... and if you really really wanted to, you of course could postfix all of your modules with mantras like del np, sys, os, quux, bar; that way those names wouldn't be importable, but... I really don't think it's worth the effort.

Upvotes: 1

Related Questions