How to make global imports from a function?
Asked Answered
B

11

91

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()
Breach answered 16/8, 2012 at 15:26 Comment(7)
Can you explain the exact use case for this?Innermost
seems like you could just import them all, then only use the modules you needLuxemburg
I assume you meant == in your conditionsBot
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.Breach
@NicolasBarbey Ops... the fingers are faster than the brain... (corrected)Breach
@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.Earleenearlene
@Luxemburg - 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.Nickolasnickolaus
W
98

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'>
Wuhu answered 16/8, 2012 at 15:39 Comment(3)
Are you sure this is legal? The docs (docs.python.org/2/reference/…) 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.Sleepwalk
global ab.cd raise a SyntaxErrorStannic
@VY, in that case you would need to use just global ab, as well, cf. my answerEarleenearlene
R
20

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

def my_imports(module_name):
    globals()[module_name] = __import__(module_name)
Raddi answered 16/8, 2012 at 16:25 Comment(7)
Also, importlib offers a wrapper for __import__ in the import_module function.Crocket
if you do globals()['ab.cd'] = __import__('ab.cd') inside function my_imports, module ab.cd will not be importedStannic
def my_imports(modulename,shortname = None): if shortname is None: shortname = modulename globals()[shortname] = __import__(modulename)Voyeurism
my_imports("rpy2")Voyeurism
my_imports("numpy","np")Voyeurism
Question: how to do a function? such as ... from rpy2.robjects.packages import importrVoyeurism
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')Familiarity
F
9

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
Folkrock answered 17/10, 2018 at 14:11 Comment(1)
will not it make as global modules also everything outside with block?Porfirioporgy
E
6

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
Earleenearlene answered 9/10, 2016 at 7:46 Comment(0)
V
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"

Voyeurism answered 22/10, 2017 at 19:52 Comment(1)
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)Roxie
S
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__)
Skinny answered 12/11, 2018 at 4:0 Comment(0)
A
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)
Aucoin answered 11/11, 2020 at 17:16 Comment(0)
O
1

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

mod == __import__(module_name)
Organdy answered 16/8, 2012 at 15:28 Comment(2)
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.Raddi
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 https://mcmap.net/q/245889/-make-all-variables-in-a-python-function-global for more ranting along these lines)Organdy
E
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
Ergograph answered 22/6, 2020 at 14:4 Comment(0)
O
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.

Ovoid answered 19/1, 2023 at 19:44 Comment(0)
S
0

May be the idea will be helpful if we want to escape warnings of a linter:

def deferred_import():
    import math
    import requests
    return {'math': math, 'requests': requests}

# Import (one time) when using.
print(deferred_import()['math'].sin(1))
Serrulate answered 17/6, 2024 at 14:5 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.