Niklas R
Niklas R

Reputation: 16870

Import module from directory without touching sys.path

I'm searching for a way to import modules from a location on the local filesystem without the requirement of appending the parent-directory to sys.path. Here's example-code showing the demanded interface:

imp = Importer()
imp.add_path(r'C:\pylibs')
foolib = imp.import_('foolib')
print foolib
# <module 'foolib' from 'C:\pylibs\foolib.py'>

I can think of an implementation like this, but I was wondering if it is possible without the workaround of exchanging the sys.path variable temporaribly.

import sys

class Importer(object):

    def __init__(self):
        super(Importer, self).__init__()
        self.path = []

    def add_path(self, path):
        self.path.append(path)

    def import_(self, name):
        old_path = sys.path
        sys.path = self.path

        try:
            return __import__(name, {}, {})
        finally:
            sys.path = old_path

Upvotes: 4

Views: 1708

Answers (3)

Niklas R
Niklas R

Reputation: 16870

I'm using localimport now:

with _localimport('lib', eggs=True) as importer:
    import some_package
assert 'some_package' not in sys.modules
assert 'some_package' in importer.modules

https://github.com/NiklasRosenstein/localimport

Upvotes: 0

Niklas R
Niklas R

Reputation: 16870

Final Code

Thanks to C0deH4cker.

import sys
import imp

class Importer(object):
    r"""
    Use this class to enable importing modules from specific
    directories independent from `sys.path`.
    """

    def __init__(self):
        super(Importer, self).__init__()
        self.path = []

    def add(self, *paths):
        r"""
        Add the passed strings to the search-path for importing
        modules. Raises TypeError if non-string object was passed.
        Passed paths are automatically expanded.
        """

        new_paths = []
        for path in paths:
            if not isinstance(path, basestring):
                raise TypeError('passed argument must be string.')
            path = os.path.expanduser(path)
            new_paths.append(path)

        self.path.extend(new_paths)

    def import_(self, name, load_globally=False):
        r"""
        Import the module with the given name from the directories
        added to the Importer. The loaded module will not be inserted
        into `sys.modules` unless `load_globally` is True.
        """

        prev_module = None
        if name in sys.modules and not load_globally:
            prev_module = sys.modules[name]
            del sys.modules[name]

        data = imp.find_module(name, self.path)
        try:
            return imp.load_module(name, *data)
        except:
            data[0].close()
            raise
        finally:
            # Restore the old module or remove the module that was just
            # loaded from `sys.modules` only if we do not load the module
            # globally.
            if not load_globally:
                if prev_module:
                    sys.modules[name] = prev_module
                else:
                    del sys.modules[name]

Upvotes: 1

C0deH4cker
C0deH4cker

Reputation: 4055

Try looking into the imp module.

Specifically, the functions

imp.find_module(name[, path])

and

imp.load_module(name, file, pathname, description)

look useful.

Upvotes: 3

Related Questions