How to import a submodule relative to the caller? - python

Importing a module can be done in a function, then passed back to the caller, take this simple utility.
def import_reload_or_none(name, reload=True):
try:
mod = __import__(name)
if reload:
import importlib
mod = importlib.reload(mod)
return mod
except Exception:
import traceback
traceback.print_exc()
return None
This works for root-level modules, however I'm not sure how this could be done when the function is in another module or when relative imports are used.
How could this function be made to work to replace for eg:
from . import my_package_module

Try passing the path to the module you want to import to your function for example:
def import_reload_or_none(name, reload=True, path=None):
mod = __import__(path + '/' + name) if path is not None else __import__(name)
if reload:
import importlib
mod = importlib.reload(mod)
return mod
Then, if you want to import with a relative path you can use
import_reload_or_none(my_package_module, path = '.')

Related

How to import module from package read in from string?

Say I have a string: 'src.utils.random_num'
How can I import random_num and actually execute it. What I have thus far:
from importlib import import_module
def parse_module(module_path):
package = '.'.join(module_path.split('.')[:-1])
module = module_path.split('.')[-1]
return package, module
req_import = parse_module('src.utils.random_num')
# import the package, works fine
module = import_module(req_import[0])
# how can I access and execute `random_num` via `req_import[1]`?
module.random_num()
How can I evaluate that random_num is a function and execute it dynamically?

How to perform a dynamic relative import in Python 3?

I have a following file structure:
mymodule/
__init__.py
mylib.py
test.py
File mymodule/__init__.py:
# mymodule/__init__.py
def call_me():
module = __import__('.mylib')
module.my_func()
File mymodule/mylib.py:
# mymodule/mylib.py
def my_func():
print("hi!")
File test.py:
# test.py
from mymodule import call_me
call_me()
If I run python3 test.py it fails with the error:
module = __import__('.mylib')
ImportError: No module named '.mylib'
I want to perform a relative import inside of call_me that equals to the static import from . import mylib. How can I do it?
Use importlib.import_module and specify your package from __name__ in __init__.py:
importlib.import_module(name, package=None)
Import a module.
The 'package' argument is required when performing a relative import. It
specifies the package to use as the anchor point from which to resolve the
relative import to an absolute import.
Example:
import importlib
def call_me():
module = importlib.import_module('.mylib', package=__name__)
module.my_func()
How about this,
def call_me():
mylib = __import__('mylib', globals(), locals(), [], 1)
mylib.my_func()
Please refer the doc: import

how can I insert a variable in a line of code?

I want to let the user to choose which file to open using a string variable. Basically I want to learn how can I tell Python to use a variable in a code section.
I have the following code:
def call_file(fn1):
import fn1
filename = input("Name of the file to import")
call_file(filename)
And inside the same folder i have the helloWorld.py with:
print("hello world")
As you have found, the import statement does not accomplish what you need. Try this instead:
from importlib import import_module
def call_file(fn1):
return import_module(fn1)
filename = input("Name of the file to import: ")
usermodule = call_file(filename)
The import_module function allows you to import a module given as an argument. The python docs have more information on this function.
Example
Running under ipython, we can use the code above to import the os module and access it under the name usermodule:
In [3]: run t.py
Name of the file to import: os
In [4]: usermodule.stat('t.py')
Out[4]: os.stat_result(st_mode=33200, st_ino=97969455, st_dev=2066, st_nlink=1, st_uid=5501, st_gid=5501, st_size=196, st_atime=1462081283, st_mtime=1462081283, st_ctime=1462081283)
Improvement
If the file the user asks for cannot be imported, the code should handle the error, possibly like this:
try:
usermodule = call_file(filename)
except ImportError:
print('Sorry, that file could not be imported.')
Alternative
It is also possible to import modules from a variable name using __import__:
>>> mod = 'math'
>>> new = __import__(mod)
>>> new.cos(0)
1.0
Note, however, that the python documentation frowns on this:
Direct use of __import__() is also discouraged in favor of
importlib.import_module().
You could also use the sys module to achieve the same effect as importing a module as some other name.
import sys
def my_import(name):
__import__(name)
return sys.modules[name]
module = my_import('random') #just for testing
print module.randint(0,1) #just for testing
Below code can be used to grab module at certain depths!
def my_import(name):
m = __import__(name)
for n in name.split(".")[1:]:
m = getattr(m, n)
return m
m = __import__("xml.etree.ElementTree") # returns xml
m = my_import("xml.etree.ElementTree") # returns ElementTree

Import hooks for PyQt4.QtCore

I'm am attempting to setup some import hooks through sys.meta_path, in a somewhat similar approach to this SO question. For this, I need to define two functions find_module and load_module as explained in the link above. Here is my load_module function,
import imp
def load_module(name, path):
fp, pathname, description = imp.find_module(name, path)
try:
module = imp.load_module(name, fp, pathname, description)
finally:
if fp:
fp.close()
return module
which works fine for most modules, but fails for PyQt4.QtCore when using Python 2.7:
name = "QtCore"
path = ['/usr/lib64/python2.7/site-packages/PyQt4']
mod = load_module(name, path)
which returns,
Traceback (most recent call last):
File "test.py", line 19, in <module>
mod = load_module(name, path)
File "test.py", line 13, in load_module
module = imp.load_module(name, fp, pathname, description)
SystemError: dynamic module not initialized properly
The same code works fine with Python 3.4 (although imp is getting deprecated and importlib should ideally be used instead there).
I suppose this has something to do with the SIP dynamic module initialization. Is there anything else I should try with Python 2.7?
Note: this applies both with PyQt4 and PyQt5.
Edit: this may be related to this question as indeed,
cd /usr/lib64/python2.7/site-packages/PyQt4
python2 -c 'import QtCore'
fails with the same error. Still I'm not sure what would be a way around it...
Edit2: following #Nikita's request for a concrete use case example, what I am trying to do is to redirect the import, so when one does import A, what happens is import B. One could indeed think that for this it would be sufficient to do module renaming in find_spec/find_module and then use the default load_module. However, it is unclear where to find a default load_module implementation in Python 2. The closest implementation I have found of something similar is future.standard_library.RenameImport. It does not look like there is a backport of the complete implementation of importlib from Python 3 to 2.
A minimal working example for the import hooks that reproduces this problem can be found in this gist.
UPD: This part in not really relevant after answer updates, so see UPD below.
Why not just use importlib.import_module, which is available in both Python 2.7 and Python 3:
#test.py
import importlib
mod = importlib.import_module('PyQt4.QtCore')
print(mod.__file__)
on Ubuntu 14.04:
$ python2 test.py
/usr/lib/python2.7/dist-packages/PyQt4/QtCore.so
Since it's a dynamic module, as said in the error (and the actual file is QtCore.so), may be also take a look at imp.load_dynamic.
Another solution might be to force the execution of the module initialization code, but IMO it's too much of a hassle, so why not just use importlib.
UPD: There are things in pkgutil, that might help. What I was talking about in my comment, try to modify your finder like this:
import pkgutil
class RenameImportFinder(object):
def find_module(self, fullname, path=None):
""" This is the finder function that renames all imports like
PyQt4.module or PySide.module into PyQt4.module """
for backend_name in valid_backends:
if fullname.startswith(backend_name):
# just rename the import (That's what i thought about)
name_new = fullname.replace(backend_name, redirect_to_backend)
print('Renaming import:', fullname, '->', name_new, )
print(' Path:', path)
# (And here, don't create a custom loader, get one from the
# system, either by using 'pkgutil.get_loader' as suggested
# in PEP302, or instantiate 'pkgutil.ImpLoader').
return pkgutil.get_loader(name_new)
#(Original return statement, probably 'pkgutil.ImpLoader'
#instantiation should be inside 'RenameImportLoader' after
#'find_module()' call.)
#return RenameImportLoader(name_orig=fullname, path=path,
# name_new=name_new)
return None
Can't test the code above now, so please try it yourself.
P.S. Note that imp.load_module(), which worked for you in Python 3 is deprecated since Python 3.3.
Another solution is not to use hooks at all, but instead wrap the __import__:
print(__import__)
valid_backends = ['shelve']
redirect_to_backend = 'pickle'
# Using closure with parameters
def import_wrapper(valid_backends, redirect_to_backend):
def wrapper(import_orig):
def import_mod(*args, **kwargs):
fullname = args[0]
for backend_name in valid_backends:
if fullname.startswith(backend_name):
fullname = fullname.replace(backend_name, redirect_to_backend)
args = (fullname,) + args[1:]
return import_orig(*args, **kwargs)
return import_mod
return wrapper
# Here it's important to assign to __import__ in __builtin__ and not
# local __import__, or it won't affect the import statement.
import __builtin__
__builtin__.__import__ = import_wrapper(valid_backends,
redirect_to_backend)(__builtin__.__import__)
print(__import__)
import shutil
import shelve
import re
import glob
print shutil.__file__
print shelve.__file__
print re.__file__
print glob.__file__
output:
<built-in function __import__>
<function import_mod at 0x02BBCAF0>
C:\Python27\lib\shutil.pyc
C:\Python27\lib\pickle.pyc
C:\Python27\lib\re.pyc
C:\Python27\lib\glob.pyc
shelve renamed to pickle, and pickle is imported by default machinery with the variable name shelve.
When finding a module which is part of package like PyQt4.QtCore, you have to recursively find each part of the name without .. And imp.load_module requires its name parameter be full module name with . separating package and module name.
Because QtCore is part of a package, you shoud do python -c 'import PyQt4.QtCore' instead. Here's the code to load a module.
import imp
def load_module(name):
def _load_module(name, pkg=None, path=None):
rest = None
if '.' in name:
name, rest = name.split('.', 1)
find = imp.find_module(name, path)
if pkg is not None:
name = '{}.{}'.format(pkg, name)
try:
mod = imp.load_module(name, *find)
finally:
if find[0]:
find[0].close()
if rest is None:
return mod
return _load_module(rest, name, mod.__path__)
return _load_module(name)
Test;
print(load_module('PyQt4.QtCore').qVersion())
4.8.6

python: how to import a generated module [duplicate]

I'm writing a Python application that takes a command as an argument, for example:
$ python myapp.py command1
I want the application to be extensible, that is, to be able to add new modules that implement new commands without having to change the main application source. The tree looks something like:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
So I want the application to find the available command modules at runtime and execute the appropriate one.
Python defines an __import__() function, which takes a string for a module name:
__import__(name, globals=None, locals=None, fromlist=(), level=0)
The function imports the module name, potentially using the given globals and locals to determine how to interpret the name in a package context. The fromlist gives the names of objects or submodules that should be imported from the module given by name.
Source: https://docs.python.org/3/library/functions.html#__import__
So currently I have something like:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
This works just fine, I'm just wondering if there is possibly a more idiomatic way to accomplish what we are doing with this code.
Note that I specifically don't want to get in to using eggs or extension points. This is not an open-source project and I don't expect there to be "plugins". The point is to simplify the main application code and remove the need to modify it each time a new command module is added.
See also: How do I import a module given the full path?
With Python older than 2.7/3.1, that's pretty much how you do it.
For newer versions, see importlib.import_module for Python 2 and Python 3.
Or using __import__ you can import a list of modules by doing this:
>>> moduleNames = ['sys', 'os', 're', 'unittest']
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)
Ripped straight from Dive Into Python.
The recommended way for Python 2.7 and 3.1 and later is to use importlib module:
importlib.import_module(name, package=None)
Import a module. The name argument specifies what module to import in absolute or relative terms (e.g. either pkg.mod or ..mod). If the name is specified in relative terms, then the package argument must be set to the name of the package which is to act as the anchor for resolving the package name (e.g. import_module('..mod', 'pkg.subpkg') will import pkg.mod).
e.g.
my_module = importlib.import_module('os.path')
Note: imp is deprecated since Python 3.4 in favor of importlib
As mentioned the imp module provides you loading functions:
imp.load_source(name, path)
imp.load_compiled(name, path)
I've used these before to perform something similar.
In my case I defined a specific class with defined methods that were required.
Once I loaded the module I would check if the class was in the module, and then create an instance of that class, something like this:
import imp
import os
def load_from_file(filepath):
class_inst = None
expected_class = 'MyClass'
mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])
if file_ext.lower() == '.py':
py_mod = imp.load_source(mod_name, filepath)
elif file_ext.lower() == '.pyc':
py_mod = imp.load_compiled(mod_name, filepath)
if hasattr(py_mod, expected_class):
class_inst = getattr(py_mod, expected_class)()
return class_inst
Using importlib
Importing a source file
Here is a slightly adapted example from the documentation:
import sys
import importlib.util
file_path = 'pluginX.py'
module_name = 'pluginX'
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Verify contents of the module:
print(dir(module))
From here, module will be a module object representing the pluginX module (the same thing that would be assigned to pluginX by doing import pluginX). Thus, to call e.g. a hello function (with no parameters) defined in pluginX, use module.hello().
To get the effect "importing" functionality from the module instead, store it in the in-memory cache of loaded modules, and then do the corresponding from import:
sys.modules[module_name] = module
from pluginX import hello
hello()
Importing a package
To import a package instead, calling import_module is sufficient. Suppose there is a package folder pluginX in the current working directory; then just do
import importlib
pkg = importlib.import_module('pluginX')
# check if it's all there..
print(dir(pkg))
Use the imp module, or the more direct __import__() function.
You can use exec:
exec("import myapp.commands.%s" % command)
If you want it in your locals:
>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'
same would work with globals()
Similar as #monkut 's solution but reusable and error tolerant described here http://stamat.wordpress.com/dynamic-module-import-in-python/:
import os
import imp
def importFromURI(uri, absl):
mod = None
if not absl:
uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
path, fname = os.path.split(uri)
mname, ext = os.path.splitext(fname)
if os.path.exists(os.path.join(path,mname)+'.pyc'):
try:
return imp.load_compiled(mname, uri)
except:
pass
if os.path.exists(os.path.join(path,mname)+'.py'):
try:
return imp.load_source(mname, uri)
except:
pass
return mod
The below piece worked for me:
>>>import imp;
>>>fp, pathname, description = imp.find_module("/home/test_module");
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();
if you want to import in shell-script:
python -c '<above entire code in one line>'
The following worked for me:
import sys, glob
sys.path.append('/home/marc/python/importtest/modus')
fl = glob.glob('modus/*.py')
modulist = []
adapters=[]
for i in range(len(fl)):
fl[i] = fl[i].split('/')[1]
fl[i] = fl[i][0:(len(fl[i])-3)]
modulist.append(getattr(__import__(fl[i]),fl[i]))
adapters.append(modulist[i]())
It loads modules from the folder 'modus'. The modules have a single class with the same name as the module name. E.g. the file modus/modu1.py contains:
class modu1():
def __init__(self):
self.x=1
print self.x
The result is a list of dynamically loaded classes "adapters".

Categories

Resources