I'm making a Python distribution package with a single import package which has multiple modules (One folder, multiple files). The package has dependencies which are imported in the modules. When a user imports a module from the import package they also get access to the dependencies which are imported outside functions. I have found a work around, but I'm not sure if it is a good idea.
from markdown import markdown
def joke():
return markdown("The Funniest Joke in the World")
def joke():
from markdown import markdown
return markdown("The Funniest Joke in the World")
>>> import joke
>>> joke.joke()
'The Funniest Joke in the World'
>>> dir(joke)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'joke', 'markdown']
>>> joke.markdown("Not funny")
'<p>Not funny</p>'
>>> import joke
>>> dir(joke)
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'joke', 'markdown']
>>> joke.markdown("Not funny")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'joke' has no attribute 'markdown'
I don't want to expose the dependencies to the users, which means option B is better. However, the code becomes messy if all imports needs to be inside classes or functions. Is there a better option for achieving the functionality in B without making the imports messy?
I have looked at some packages to see if I can locate any of theirs dependencies using dir() but feel a bit overwhelmed since I do not know which dependencies they have or how to find their dependencies.
Starting with Python 3.7 you can define a module-level __dir__
function which will be used for generating results for dir(module)
:
def __dir__():
return ['joke']
This can be combined with __all__
which handles from module import *
imports:
__all__ = ['joke']
def __dir__():
return __all__
Note that users will still be able to access these dependency packages, i.e. joke.markdown
still works. If you really want to restrict this, you can also define a module-level __getattr__
:
def __getattr__(name):
if name not in __all__:
raise AttributeError(name)
return globals()[name]