6

Description

Assume, I have a package with the following structure:

package-folder
  |_ mypackage
     |_ __init__.py
     |_ module1.py
  setup.py

Where __init__.py is empty and

module1.py:

def do_stuff():
    print('Did stuff.')

setup.py is containing the usual context for making a package out of this, which can be installed with pip install -e pkgpath on the local system.

In a script.py, I would have to do:

import mypackage as abbr

abbr.module1.do_stuff()
# output: Did stuff.

My desired call would be:

abbr.do_stuff()
# output: Did stuff.

Questions

  1. How can I 'organize' my package so that I can call functions, out of module1.py directly from abbr.function()? What code changes are required?
  2. Is this organization useful if the package grows? So that directories would act as the structure-element and python-files are just capsules to organize my functions within?
  3. What is the best way to organize functions in modules and sub-modules?

Further thoughts

  • Is there a good reason not to have just one function in each python-file which can be used from the module? (and has the same name as the file)
  • I assume, I will have to modify my __init__.py-files, but I couldn't figure out how.

Clarification on questions 2

package-folder
|_ mypackage
   |_ __init__.py
   |_ module1.py #with function do_stuff1()
   |_ subpackage
      |_module2.py #with function do_stuff2()
setup.py

Calls should be:

abbr.do_stuff1()
abbr.subpackage.do_stuff2()

And not:

abbr.module1.do_stuff1()
abbr.subpackage.module2.do_stuff2()
Laenan
  • 375
  • 1
  • 2
  • 12

1 Answers1

3

You got quite close to the answer, you just needed to import the methods from __init__.py.

You can also add the special variable __all__ that is a list of the variable/functions/etc that the user can expect to find in that file.

__init__.py @ mypackage

from .module1 import do_stuff1

__all__ = ['do_stuff1']

__init__.py @ subpackage

from .module2 import do_stuff2

__all__ = ['do_stuff2']

EDIT:

To make your top-level package import all the subpackage symbols you could change __init__.py @ mypackage to:

import pkgutil

from .module1 import do_stuff1

__all__ = ['do_stuff']

for loader, name, is_pkg in  pkgutil.walk_packages(__path__):
    module = loader.find_module(name).load_module(name)
    __all__.extend(module.__all__)
    for var in module.__all__:
        globals()[var] = getattr(module, var)

Then you will not only be able to call abbr.subpackage.do_stuuf2() but also abbr.do_stuuf2().

Adirio
  • 5,040
  • 1
  • 14
  • 26
  • Your solutions works, concerning the question 1! Great! But wouldn't this get a bit messy, with a 'big' package? Isn't there a better way to do that? Also, in [here](https://stackoverflow.com/questions/3365740/how-to-import-all-submodules) was a code snipped suggested, which should load 'all' submodules, but I couldn't get it to work. Besides that, some 'guides' recommend to better leave your __init__.py empty. Last comment: But what is the 'best' way to do it? – Laenan Jan 11 '18 at 14:37
  • I wouldn't consider leaving `__init__.py` empty a recomendation, it's more a option you have, but it gives you a really powerfull way of offering easy object access while keeping the code in an organized fashion. I do use scripts instead of manual imports in some `__init__.py` but only too flattern subpackages, this is, being able to do `abbr.do_stuff2()`. – Adirio Jan 11 '18 at 16:22
  • Could you give an example script, you use for that? – Laenan Jan 12 '18 at 10:43
  • @Laenan I do not have a copy of the script on me right now, but I basically iter over all the subpackages (directories) importing all that its in their __all__ variable into the main package. – Adirio Jan 12 '18 at 10:46
  • 1
    @Laenan I added an unchecked script to my answer, check if that works for you. It will basically get everything in the `__all__` variable of subpackages and also place in the package `__all__`. – Adirio Jan 12 '18 at 11:09
  • Hey thanks @Adirio! Will have a look into that on monday. – Laenan Jan 13 '18 at 17:46
  • Hey, tested a bunch of stuff. Works fine for minimal example, but not in my code. But your first answer is sufficient for my problem. :-) Thank's again! – Laenan Jan 22 '18 at 17:03