2

Given a path to a file, I need the package name to pass to importlib.import_module() so that relative imports will work correctly. I can't import it and then check module.__package__ because it won't import successfully.

matsjoyce
  • 5,744
  • 6
  • 31
  • 38
ceridwen
  • 550
  • 2
  • 5
  • 14

2 Answers2

2

Here's one rather generic method:

import pathlib
import sys


def get_module_name(path):
    f = pathlib.Path(path).resolve()
    for i in map(pathlib.Path, sys.path):
        try:
            f.relative_to(i)
        except ValueError:
            pass
        else:
            *parts, fname = f.relative_to(i).parts
            return ".".join(parts), [f.stem]

module, fromlist = get_module_name("Programming/Python/kernprof.py")

print(module, fromlist)

imported_module = __import__(module, fromlist=fromlist)

print(imported_module)
print(getattr(imported_module, fromlist[0]))

Outputs:

Programming.Python ['kernprof']
<module 'Programming.Python' (namespace)>
<module 'Programming.Python.kernprof' from '/home/matthew/Programming/Python/kernprof.py'>

This solution can handle import with any path from sys.path, but cannot do relative imports (imports above the sys.path). For the how __import__ is used, see Why does Python's __import__ require fromlist?.

Community
  • 1
  • 1
matsjoyce
  • 5,744
  • 6
  • 31
  • 38
  • This method has some limitations because it makes a static determination using the directory structure without processing __init__.py files or other ways to monkey with deciding which files belong to which package, but it's good enough for my purposes. – ceridwen Apr 28 '15 at 17:32
  • Yeah, processing `__init__`s and co would be pretty complicated, with some possibly unwanted results. – matsjoyce Apr 29 '15 at 15:51
0

What about this helper ?

import os


def get_parent_package(path):
    parent_path = os.path.split(path)[0]
    while parent_path != os.sep:
        if '__init__.py' in os.listdir(parent_path):
            return os.path.basename(parent_path)
        parent_path = os.path.split(parent_path)[0]
    return None

parent_path != os.sep must be improved if you are under windows.

Ali SAID OMAR
  • 6,404
  • 8
  • 39
  • 56
  • The major problem with this, as I discovered, is that in Python 3.3+ __init__.py is no longer needed to define a module, see Nick Coghlan's discussion at http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html. There can be other problems with traversing the directory tree from the bottom up for packages containing subpackages. – ceridwen Apr 28 '15 at 17:29