1

I'm currently refactoring a Python 2 project which includes the ability to add or remove plugins, which are Python modules implementing a given API.

The main app accesses add/remove/update hooks in plugins by calling e.g. __import__('plugin_name').hook_add() after pulling plugin names from a database, but this seems like a hacky approach. Having said that, I don't know of better ways to programatically access plugins.

Is this considered an antipattern in Python?

1
  • 1
    There's nothing inherently wrong with __import__. You could possibly use the importlib module instead, but it isn't necessarily a better alternative. If you think it is a hacky approach, maybe you can try to design a better solution and see if it is clearer and easier to understand than the current code. May 17, 2016 at 12:49

1 Answer 1

3

Is using __import__('module_name') an antipattern in Python?

The api for __import__ is somewhat misleading. I personally would prefer to avoid it where possible.

The docs for Python 3 state:

Direct use of __import__() is also discouraged in favor of importlib.import_module().

Here's the API for __import__:

mod = __import__(
    module, # string of dotted name
    globals=None, # only needed to do relative import
    locals=None, # implementation ignores this
    fromlist=()) # fromlist just needs to be non-empty... :(
    level=0 # relative import, not going into this part
    )

If fromlist is empty, you import the module, but get returned the root package like this, so you have to do the dotted lookup to get back to it:

>>> foo = __import__('foo.bar.baz')
>>> foo.bar.baz
<module 'foo.bar.baz' from /.../foo/bar/baz.py>

This is the same as

>>> import foo.bar.baz
>>> foo.bar.baz
<module 'foo.bar.baz' from /.../foo/bar/baz.py>

If you just want the module, you need fromlist to be non-empty:

>>> baz = __import__('foo.bar.baz', fromlist=[None])
>>> baz
<module 'foo.bar.baz' from /.../foo/bar/baz.py>

Which is the same as

>>> from foo.bar import baz
>>> baz
<module 'foo.bar.baz' from /.../foo/bar/baz.py>

>>> baz is foo.bar.baz
True

Use importlib.import_module instead:

Here's the usage for import_module:

>>> from importlib import import_module
>>> baz = import_module('foo.bar.baz')

That's much nicer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.