15

As discussed here, we can dynamically import a module using string variable.

import importlib
importlib.import_module('os.path')

My question is how to import * from string variable?

Some thing like this not working for now

importlib.import_module('os.path.*')
martineau
  • 119,623
  • 25
  • 170
  • 301
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
  • 2
    Why are you trying to do this? `*`-imports are generally frowned upon, they clutter the namespace and you may end up importing things you did not intend (e.g. a module is updated, gets a new function that overrides an earlier import). Do you have a concrete usecase for doing this dynamically? – mata Jun 12 '17 at 07:07
  • I know using `import *` sound dangerous; though it works for simple use case. My concrete usecase is that I'm trying to use `proboscis` to run `python test` which allow me to pick up which test suit to run - the chosen test suit is defined by an array of test filenames. – Nam G VU Jun 12 '17 at 07:18

1 Answers1

25

You can do the following trick:

>>> import importlib
>>> globals().update(importlib.import_module('math').__dict__) 
>>> sin
<built-in function sin>

Be warned that makes all names in the module available locally, so it is slightly different than * because it doesn't start with __all__ so for e.g. it will also override __name__, __package__, __loader__, __doc__.

Update:

Here is a more precise and safer version as @mata pointed out in comments:

module = importlib.import_module('math')

globals().update(
    {n: getattr(module, n) for n in module.__all__} if hasattr(module, '__all__') 
    else 
    {k: v for (k, v) in module.__dict__.items() if not k.startswith('_')
})

Special thanks to Nam G VU for helping to make the answer more complete.

hurturk
  • 5,214
  • 24
  • 41
  • I don't get what you mean by `it is slightly different than * because it doesn't start with __all__` - what is **it** here? – Nam G VU Jun 12 '17 at 06:58
  • 2
    This will also import things like `__name__`, `__package__`, `__loader__`, `__doc__` etc. which you definitely do not want imported. – mata Jun 12 '17 at 07:03
  • 7
    more than a sin – hurturk Jun 12 '17 at 07:07
  • I see, may you update to clarify that and get accepted ^^ ? – Nam G VU Jun 12 '17 at 07:14
  • 3
    `if not k.startswith('_')` would probably be better. The real import machinery when doing a `*` import also checks if the module has an `__all__` attribute and only imports what is given there. – mata Jun 12 '17 at 07:36
  • 1
    Thanks for the trick! Looks better now, I just skipped `__all__` to make it a bit short snippet but your advice is good enough also to hide functions start with `_` – hurturk Jun 12 '17 at 07:42
  • You two are brilliant! – Nam G VU Jun 12 '17 at 09:01
  • @mata Lovely if you can help to modify @hurturk answer to include the `__all__` checking part. Thank you so much! – Nam G VU Jun 12 '17 at 09:02
  • 1
    Something in the line of `module = importlib.import_module('math'); globals().update({n: getattr(module, n) for n in module.__all__} if hasattr(module, '__all__') else {k: v for (k, v) in module.__dict__.items() if not k.startswith('_')})`, only [formatted nicer](https://pastebin.com/MiPwwjR8) – mata Jun 12 '17 at 10:23
  • Hello, restructured the answer, please let me know if you want to keep previous one but I suppose this looks easier to read now. Thank you Nam G VU for your contribution. – hurturk Jun 12 '17 at 13:46