Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a `from ... import *' api seperate from __all__ in a module?

I have a module which is split roughly in half between utility type functions and core api functions. I need to have all of them in __all__ in order to have help() be useful, and also to specify which of the many functions/classes/etc in the module are for external use, but I also want to support from mymodule import * as a way to get just the core functionality into other modules. Is there a way to do that?

like image 383
Ethan Furman Avatar asked Aug 15 '11 17:08

Ethan Furman


People also ask

How do you define __ all __?

PACKAGES. In the __init__.py file of a package __all__ is a list of strings with the names of public modules or other objects. Those features are available to wildcard imports. As with modules, __all__ customizes the * when wildcard-importing from the package.

What does __ import __ do in Python?

The __import__() in python module helps in getting the code present in another module by either importing the function or code or file using the import in python method. The import in python returns the object or module that we specified while using the import module.

Which statement is used to import all the items from a module?

import * statement. You can import all attributes of a module using this statement. This will make all attributes of imported module visible in your code.

Can I import from __ init __ py?

If a file named __init__.py is present in a package directory, it is invoked when the package or a module in the package is imported. You can use this to execute package initialization code, for example for the initialization of package-level data.


2 Answers

Almost. While you can't have __all__ do double duty in this way, you can add your own virtual api module which can then be imported...

class fake_module(object):
    def __init__(self, name, *args):
        self.name = name
        self.__all__ = []
        all_objects = globals()
        for name in args:
            self.__dict__[name] = all_objects[name]
            self.__all__.append(name)
    def register(self):
        sys.modules["%s.%s" % (__name__, self.name)] = self

    fake_module('api', 'class1', 'class2', 'func3', 'exception4').register()

Then in the other modules instead of from mymodule import * you can do from mymodule.api import * to get just the desired subset while still keeping everything in a single module.

Note: from ... import * is not usually good practice, and should be used with great care and only with modules/packages that explicity state that they have been designed with such usage in mind.

like image 179
Ethan Furman Avatar answered Oct 23 '22 19:10

Ethan Furman


Ethan, your fake_module is likely better accomplished with plain old python. Just move your code into a directory setup:

mymodule
    __init__.py
    api.py
    util.py
    _mymodule.py

Where _mymodule.py contains your current code.

In your __init__.py:

from _mymodule import * 

In you're api.py

# explicity import only those api classes you want to expose
# this will allow you to call `from mymodule.api import *`
import from _mymodule ApiClass1
import from _mymodule ApiClass2
# etc  

Do the same for your util.py

# explicity import only those util classes you want to expose
# this will allow you to call `from mymodule.util import *`
import from _mymodule UtilClass1
import from _mymodule UtilClass2
# etc

So now you can:

# import everything willy nilly for your help()
from mymodule import *

# or import only the "public" values from api and util
from mymodule.api import *
from mymodule.util import *

Breaking up your code like this can be useful for organizing a largish module that you still want to be able to access from a top level namespace. But I would have to agree with brandizzi that using from mymodule import * in code that is using this module is generally a bad idea. What you gain in ease of importing you lose in transparency of the code that is using those imports.

like image 28
Mark Gemmill Avatar answered Oct 23 '22 17:10

Mark Gemmill