Filtered wildcard imports?

Hi all, yet another (possibly bad?) idea from day-to-day work ... I occasionally need to import a lot of "stuff" from certain modules. The "stuff" is usually following a pattern. E.g. I have modules that (mostly) collect special exception classes and I usually need them all in one push - but nothing else from those modules. So a general wildcard import is a bad idea in a lot of ways: ```python from somemodule import * ``` What I usually do instead is something along the following lines: ```python import somemodule as _somemodule _globals = globals() for _attr in dir(_somemodule): # looking for "stuff" if not _attr.endswith('Error'): # the filter - this is not "stuff" continue _globals[_attr] = getattr(_somemodule, _attr) # the "import" del _globals, _attr, _somemodule # some cleanup ``` The above selects and "imports" everything ending on "Error" into the global namespace. What I would love to do instead is something like a "filtered wildcard import", maybe through regular expressions or shell-like matching: ```python from somemodule import *Error ``` Best regards, Sebastian

Why not just import the module they're defined in and then just do attribute access syntax? from mod.modb.dtx import somemod somemod.SomeError

On Sun, Aug 02, 2020 at 03:36:44PM +0200, Sebastian M. Ernst wrote:
When I have a similar requirement, I do something like this: stuff_to_import = ['SomeError', 'AnotherError'] exec("from somemodule import %s" % ', '.join(stuff_to_import)) del stuff_to_import (by memory, so the details may not be exactly correct). Another possibility is to offer the stuff to import in the library module: # somemodule.py errors = ['SomeError', 'AnotherError'] # caller from somemodule import errors as errors exec("from somemodule import %s" % ', '.join(errors)) del errors The list of stuff to import can even be generated programmatically, by name or functionality: # the very end of somemodule.py errors = [name for name, obj in globals().items() if isinstance(obj, type) and issubclass(obj, Exception)] Another option: # Caller import somemodule globals().update({name: obj for name, obj in vars(somemodule).items() if isinstance(obj, type) and issubclass(obj, Exception)}) The bottom line here is that we can be far more flexible about filtered imports than mere wild cards in the names, and in a lot fewer lines of code than you've been using. -- Steven

On 02.08.20 15:36, Sebastian M. Ernst wrote:
It seems the responsibility of the package / distribution you are using to provide appropriate namespaces for their objects. If this "stuff" is following a certain pattern it seems to be distinct from other parts of the module, especially since you as a user have the need to only import those objects. So if they lived in their own namespace then you could simply do from somepkg.errors import *

On Mon, 3 Aug 2020 at 08:26, Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:
And of course if they don't do that, you can yourself do: somepkg_errors.py ``` import somepkg __all__ = [x for x in dir(somepkg) if x.endswith('Error')] ``` Your program ``` from somepkg_errors import * ``` Paul

Why not just import the module they're defined in and then just do attribute access syntax? from mod.modb.dtx import somemod somemod.SomeError

On Sun, Aug 02, 2020 at 03:36:44PM +0200, Sebastian M. Ernst wrote:
When I have a similar requirement, I do something like this: stuff_to_import = ['SomeError', 'AnotherError'] exec("from somemodule import %s" % ', '.join(stuff_to_import)) del stuff_to_import (by memory, so the details may not be exactly correct). Another possibility is to offer the stuff to import in the library module: # somemodule.py errors = ['SomeError', 'AnotherError'] # caller from somemodule import errors as errors exec("from somemodule import %s" % ', '.join(errors)) del errors The list of stuff to import can even be generated programmatically, by name or functionality: # the very end of somemodule.py errors = [name for name, obj in globals().items() if isinstance(obj, type) and issubclass(obj, Exception)] Another option: # Caller import somemodule globals().update({name: obj for name, obj in vars(somemodule).items() if isinstance(obj, type) and issubclass(obj, Exception)}) The bottom line here is that we can be far more flexible about filtered imports than mere wild cards in the names, and in a lot fewer lines of code than you've been using. -- Steven

On 02.08.20 15:36, Sebastian M. Ernst wrote:
It seems the responsibility of the package / distribution you are using to provide appropriate namespaces for their objects. If this "stuff" is following a certain pattern it seems to be distinct from other parts of the module, especially since you as a user have the need to only import those objects. So if they lived in their own namespace then you could simply do from somepkg.errors import *

On Mon, 3 Aug 2020 at 08:26, Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:
And of course if they don't do that, you can yourself do: somepkg_errors.py ``` import somepkg __all__ = [x for x in dir(somepkg) if x.endswith('Error')] ``` Your program ``` from somepkg_errors import * ``` Paul
participants (5)
-
Dominik Vilsmeier
-
Paul Moore
-
Sebastian M. Ernst
-
Steven D'Aprano
-
William Pickard