Skip to content

Instantly share code, notes, and snippets.

@kivava
Forked from mportesdev/python_source_import.rst
Created March 26, 2025 02:43
Show Gist options
  • Select an option

  • Save kivava/cf4b41bf1d5b65d57eaac82beeb1a725 to your computer and use it in GitHub Desktop.

Select an option

Save kivava/cf4b41bf1d5b65d57eaac82beeb1a725 to your computer and use it in GitHub Desktop.
Importing Python source code from a script without the .py extension

Importing Python source code from a script without the .py extension

Generally, to import a python module programatically when you know the file's path, you could do something like this:

import importlib.util
import sys


def import_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)

    sys.modules[module_name] = module    # this step is not necessary, but it's
                                         # probably not a bad idea to have a manually
                                         # imported module stored in sys.modules
    spec.loader.exec_module(module)
    return module


foo = import_from_file('foo', 'path/to/foo.py')

However, if the file doesn't have a .py extension, Python won't know that your file contains a Python source code, and import will fail. The reason is that the file extensions recognized by the import machinery are more or less hard-coded in the source code of importlib. As a result, importlib won't know which loader to use for an unknown file type, and the spec_from_file_location method will return None.

A possible solution is to create a SourceFileLoader object and pass it as the loader parameter to the spec_from_file_location method:

from importlib.machinery import SourceFileLoader
...

def import_from_file(module_name, file_path):
    loader = SourceFileLoader(module_name, file_path)
    spec = importlib.util.spec_from_file_location(module_name, loader=loader)
    ...

Note however that you no longer need to pass file_path to spec_from_file_location, as this information is already stored in the loader object. Thanks to this, the function can be rewritten in a slightly more straightforward manner by replacing spec_from_file_location with spec_from_loader:

...

def import_from_file(module_name, file_path):
    loader = SourceFileLoader(module_name, file_path)
    spec = importlib.util.spec_from_loader(module_name, loader)
    ...
@kivava
Copy link
Author

kivava commented Mar 26, 2025

@kivava
Copy link
Author

kivava commented Mar 26, 2025

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
def import_from_file(module_name, file_path):
    try:
        spec = spec_from_loader(module_name, SourceFileLoader(module_name, file_path))
        module = module_from_spec(spec)

        sys.modules[module_name] = module    # this step is not necessary, but it's
                                            # probably not a bad idea to have a manually
                                            # imported module stored in sys.modules
        spec.loader.exec_module(module)
        return module
    except Exception as e:
        print(e)
        return None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment