Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding module imports in package

Tags:

python

I have a small package that has a few dependencies such as pandas and gensim. The file structure is like so

/package
    __init__.py
    agg_clean.py

In the __init__.py file, I have import agg_clean so I am able to access the functions in a chained manner, e.g.:

import package as pkg
pkg.agg_clean.function()

However, I have a couple of import statements in agg_clean.py, (pandas, os, etc.). when I do a chained import as above, I am also able to access the imports in agg_clean, e.g. pkg.agg_clean.os.listdir()

How can I hide imports from my various modules in the package import?

like image 449
Mike Avatar asked Oct 22 '14 20:10

Mike


1 Answers

tl;dr: You can't cleanly. Or more accurately, you shouldn't be worried about things like this.

There's no namespace collisions in this case, since the os module is loaded under then name pkg.agg_clean.os. If the user wants to use your imported os package, there is no harm in letting them do that. On top of that, there's no good way to prevent them from doing that.

There's something interesting to remember here. Take the following python module that can be imported by another python script:

# module_im_importing.py
import os as _os

__all__ = ["echo"]

name_not_available = 'some_constant_string'

def echo(object):
    pass

If your user imports the bare module using import module_im_importing, all defined variables are made available to their environment under the name module_im_importing.

>>> import module_im_importing
>>> dir()  # Lookup local namespace
['__builtins__', '__doc__', '__name__', '__package__', 'module_im_importing']
>>> dir(module_im_importing)  # See defined items in your package.
['__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'echo', 'name_not_available', 'os']

If they instead from module_im_importing import * (which is discouraged), the behavior is a little different:

>>> from module_im_importing import *
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'echo']

This causes the names defined in the file module_im_importing.py to be imported into the user's local namespace. Because we used __all__ in your module, only the items defined in __all__ are imported to the users local namespace from your module. This can pollute their namespace if, for example, you've defined a function like echo which conflicts with another package they may be using.

This is why Wildcard imports are clearly frowned upon in PEP8. From the document:

Wildcard imports (from <module> import *) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools.

In any case, please don't worry yourself over things like this. It's only an issue if the user of your module doesn't follow PEP8, and they should probably be shown the errors of their ways anyway. Remember, in Python, We're all consenting adults here.

like image 110
VooDooNOFX Avatar answered Sep 28 '22 13:09

VooDooNOFX