87

I fear that this is a messy way to approach the problem but...

let's say that I want to make some imports in Python based on some conditions.

For this reason I want to write a function:

def conditional_import_modules(test):
    if test == 'foo':
        import onemodule, anothermodule
    elif test == 'bar':
        import thirdmodule, and_another_module
    else:
        import all_the_other_modules

Now how can I have the imported modules globally available?

For example:

conditional_import_modules(test='bar')
thirdmodule.myfunction()
martineau
  • 119,623
  • 25
  • 170
  • 301
Giovanni Di Milia
  • 13,480
  • 13
  • 55
  • 67
  • 1
    Can you explain the exact use case for this? – sean Aug 16 '12 at 15:28
  • 2
    seems like you could just import them all, then only use the modules you need – Will Aug 16 '12 at 15:30
  • I assume you meant == in your conditions – Nicolas Barbey Aug 16 '12 at 15:33
  • I don't have a real use case (meaning I can solve in a different way) but this question came in my mind while I was writing some code to import some blueprints based on a configuration file in a flask web-application. I was thinking to write a function to make the imports and another to register them. – Giovanni Di Milia Aug 16 '12 at 15:37
  • @NicolasBarbey Ops... the fingers are faster than the brain... (corrected) – Giovanni Di Milia Aug 16 '12 at 15:38
  • 1
    @sean, a real-world use case: a python app that is optionally able to plot a graph with `matplotlib.pyplot`. Unconditionally importing that module may fail on some systems because it isn't installed or if it is installed it loads all installed backends, including some that might need X or tkinter, which in turn might be missing, resulting in an abort due to a thrown exception. Thus, it makes sense to only import it if the user actually requests creating a plot. – maxschlepzig Oct 09 '16 at 07:54
  • 1
    @Will - I'm not OP, but I'm facing a similar situation. In my case, importing BeautifulSoup causes a noticable delay in my script - I'd rather do some upfront validation of the inputs to make sure that the script is likely to succeed (or crash out early, if not), *before* importing it. – scubbo Nov 09 '19 at 20:20

10 Answers10

97

Imported modules are just variables - names bound to some values. So all you need is to import them and make them global with global keyword.

Example:

>>> math
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined
>>> def f():
...     global math
...     import math
...
>>> f()
>>> math
<module 'math' from '/usr/local/lib/python2.6/lib-dynload/math.so'>
Roman Bodnarchuk
  • 29,461
  • 12
  • 59
  • 75
  • 7
    Are you sure this is legal? The docs (https://docs.python.org/2/reference/simple_stmts.html#grammar-token-global_stmt) say "Names listed in a global statement must not be defined as formal parameters or in a for loop control target, class definition, function definition, or import statement." It then says this is not enforced for cpython, but you shouldn't do this. – xioxox Feb 17 '15 at 09:36
  • global ab.cd raise a SyntaxError – V Y Jul 18 '16 at 19:00
  • 1
    @VY, in that case you would need to use just `global ab`, as well, cf. [my answer](http://stackoverflow.com/a/39941236/427158) – maxschlepzig Oct 09 '16 at 07:56
18

You can make the imports global within a function like this:

def my_imports(module_name):
    globals()[module_name] = __import__(module_name)
badzil
  • 3,440
  • 4
  • 19
  • 27
  • 1
    Also, `importlib` offers a wrapper for `__import__` in the [`import_module` function](https://docs.python.org/2.7/library/importlib.html#importlib.import_module). – metakermit Sep 02 '14 at 13:57
  • if you do globals()['ab.cd'] = __import__('ab.cd') inside function my_imports, module ab.cd will not be imported – V Y Jul 18 '16 at 19:03
  • def my_imports(modulename,shortname = None): if shortname is None: shortname = modulename globals()[shortname] = __import__(modulename) – mshaffer Oct 22 '17 at 19:36
  • my_imports("rpy2") – mshaffer Oct 22 '17 at 19:36
  • my_imports("numpy","np") – mshaffer Oct 22 '17 at 19:36
  • Question: how to do a function? such as ... from rpy2.robjects.packages import importr – mshaffer Oct 22 '17 at 19:37
  • def my_imports(module_name, short_name=None): if short_name is not None: globals()[short_name] = __import__(module_name) elif short_name is None: globals()[module_name] = __import__(module_name) else: print('import error') – June Aug 18 '20 at 16:41
7

I've just had the similar problem, here is my solution:

class GlobalImport:

    def __enter__(self):
        return self

    def __call__(self):
        import inspect
        self.collector = inspect.getargvalues(inspect.getouterframes(inspect.currentframe())[1].frame).locals

    def __exit__(self, *args):
        globals().update(self.collector)

then, anywhere in the code:

with GlobalImport() as gi:
    import os, signal, atexit, threading, _thread
    # whatever you want it won't remain local
    # if only 
    gi()
    # is called before the end of this block

# there you go: use os, signal, ... from whatever place of the module
rafał grabie
  • 91
  • 1
  • 3
5

You can use the built-in function __import__ to conditionally import a module with global scope.

To import a top level module (think: import foo):

def cond_import():
  global foo
  foo = __import__('foo', globals(), locals()) 

Import from a hierarchy (think: import foo.bar):

def cond_import():
  global foo
  foo = __import__('foo.bar', globals(), locals()) 

Import from a hierarchy and alias (think: import foo.bar as bar):

def cond_import():
  global bar
  foo = __import__('foo.bar', globals(), locals()) 
  bar = foo.bar
maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
3

I like @badzil approach.

def global_imports(modulename,shortname = None, asfunction = False):
    if shortname is None: 
        shortname = modulename
    if asfunction is False:
        globals()[shortname] = __import__(modulename)
    else:        
        globals()[shortname] = eval(modulename + "." + shortname)

So something that is traditionally in a class module:

import numpy as np

import rpy2
import rpy2.robjects as robjects
import rpy2.robjects.packages as rpackages
from rpy2.robjects.packages import importr

Can be transformed into a global scope:

global_imports("numpy","np")

global_imports("rpy2")
global_imports("rpy2.robjects","robjects")
global_imports("rpy2.robjects.packages","rpackages")
global_imports("rpy2.robjects.packages","importr",True)

May have some bugs, which I will verify and update. The last example could also have an alias which would be another "shortname" or a hack like "importr|aliasimportr"

mshaffer
  • 959
  • 1
  • 9
  • 19
  • You may want to use global_imports(modulename) inside of else block before the assignment. Thanks for the code, it works. else: self.global_imports(modulename) globals()[shortname] = eval(modulename + "." + shortname) – Alihan ÖZ Aug 14 '21 at 19:26
3

I like @rafał grabie approach. As it even support importing all. i.e. from os import *

(Despite it being bad practice XD )

Not allowed to comment, but here is a python 2.7 version.

Also removed the need to call the function at the end.

class GlobalImport:
    def __enter__(self):
        return self
    def __exit__(self, *args):
        import inspect
        collector = inspect.getargvalues(inspect.getouterframes(inspect.currentframe())[1][0]).locals
        globals().update(collector)

def test():
    with GlobalImport() as gi:
        ## will fire a warning as its bad practice for python. 
        from os import *

test()
print path.exists(__file__)
Jan
  • 91
  • 5
2

I like the answer from @maxschlepzig.

There is a bug in the approach that if you directly import a function it will not work. For example,

global_imports("tqdm", "tqdm, True)

does not work, because the module is not imported. And this

global_imports("tqdm")
global_imports("tqdm", "tqdm, True)

works.

I change @maxschlepzig's answer a bit. Using fromlist so you can load function or module with "From" statement in a uniform way.

def global_imports(object_name: str,
                   short_name: str = None,
                   context_module_name: str = None):
    """import from local function as global import

    Use this statement to import inside a function,
    but effective as import at the top of the module.

    Args:
        object_name: the object name want to import,
                     could be module or function
        short_name: the short name for the import
        context_module_name: the context module name in the import

    example usage:
    import os -> global_imports("os")
    import numpy as np -> global_imports("numpy", "np")
    from collections import Counter ->
        global_imports("Counter", None, "collections")
    from google.cloud import storage ->
        global_imports("storage", None, "google.cloud")

    """
    if not short_name:
        short_name = object_name
    if not context_module_name:
        globals()[short_name] = __import__(object_name)
    else:
        context_module = __import__(context_module_name,
                                    fromlist=[object_name])
        globals()[short_name] = getattr(context_module, object_name)
Yeu-Chern Harn
  • 126
  • 1
  • 4
1

You could have this function return the names of the modules you want to import, and then use

mod == __import__(module_name)
ChrisB
  • 4,628
  • 7
  • 29
  • 41
  • I like the approach but your code wouldn't actually work in this case. This code just returns the module but doesn't actually put in the global variables. See my answer for how to do it. – badzil Aug 16 '12 at 19:46
  • I understand that the response doesn't quite answer the OP's question. However, I generally dislike manipulating globals(). Better to programmatically import the correct modules at the proper scope, IMO (see http://stackoverflow.com/a/11543718/1332492 for more ranting along these lines) – ChrisB Aug 16 '12 at 20:11
1

Step-1: config.py, config_v2.py, rnd.py in same directory/folder

Step-2: config.py

HIGH_ATTENDANCE_COUNT_MIN = 0

Step-3: config_v2.py

HIGH_ATTENDANCE_COUNT_MIN = 5

Step-4: rnd.py

def versioning_test(v):
    global config

    if v == 'v1':
        config = __import__('config', globals(), locals()) 
    
    if v == 'v2':
        config = __import__('config_v2', globals(), locals())     

def version_test_in_another_function():
    print('version_test_in_another_function: HIGH_ATTENDANCE_COUNT_MIN: ', config.HIGH_ATTENDANCE_COUNT_MIN)
 

versioning_test("v2")
version_test_in_another_function()

Step-5: $ python3 rnd.py

<<output>>: version_test_in_another_function: HIGH_ATTENDANCE_COUNT_MIN:  5
ArunDhwaj IIITH
  • 3,833
  • 1
  • 24
  • 14
0

It is now recommended (for Python 3), to use the importlib https://docs.python.org/3/reference/import.html#importlib

eg: globals()["np"] = importlib.import_module("numpy") and you can now execute "np.array([1,2,3])" afterwards.

There are also other ways of importing that you might prefer. Consider seeing the aforementioned documentation.

Ram
  • 158
  • 1
  • 16