title: Python 3.5+: How to dynamically import a module given the full file path (in the presence of implicit sibling imports)?
body: <h1>Question</h1>
  
  <p>The standard library clearly documents <a href="https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly" rel="noreferrer">how to import source files directly</a> (given the absolute file path to the source file), but this approach does not work if that source file uses implicit sibling imports as described in the example below.</p>
  
  <p>How could that example be adapted to work in the presence of implicit sibling imports?</p>
  
  <p>I already checked out <a href="https://stackoverflow.com/a/67692/1490091">this</a> and <a href="https://stackoverflow.com/a/37124336/1490091">this other</a> Stackoverflow questions on the topic, but they do not address implicit sibling imports <em>within</em> the file being imported by hand.</p>
  
  <h1>Setup/Example</h1>
  
  <p>Here's an illustrative example</p>
  
  <p>Directory structure:</p>
  
  <pre><code>root/
    - directory/
      - app.py
    - folder/
      - implicit_sibling_import.py
      - lib.py
  </code></pre>
  
  <hr>
  
  <p><code>app.py</code>:</p>
  
  <pre><code>import os
  import importlib.util
  
  # construct absolute paths
  root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
  isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
  
  def path_import(absolute_path):
     '''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
     spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
     module = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(module)
     return module
  
  isi = path_import(isi_path)
  print(isi.hello_wrapper())
  </code></pre>
  
  <hr>
  
  <p><code>lib.py</code>:</p>
  
  <pre><code>def hello():
      return 'world'
  </code></pre>
  
  <hr>
  
  <p><code>implicit_sibling_import.py</code>:</p>
  
  <pre><code>import lib # this is the implicit sibling import. grabs root/folder/lib.py
  
  def hello_wrapper():
      return "ISI says: " + lib.hello()
  
  #if __name__ == '__main__':
  #    print(hello_wrapper())
  </code></pre>
  
  <hr>
  
  <p>Running <code>python folder/implicit_sibling_import.py</code> with the <code>if __name__ == '__main__':</code> block commented out yields <code>ISI says: world</code> in Python 3.6.</p>
  
  <p>But running <code>python directory/app.py</code> yields:</p>
  
  <pre><code>Traceback (most recent call last):
    File "directory/app.py", line 10, in &lt;module&gt;
      spec.loader.exec_module(module)
    File "&lt;frozen importlib._bootstrap_external&gt;", line 678, in exec_module
    File "&lt;frozen importlib._bootstrap&gt;", line 205, in _call_with_frames_removed
    File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in &lt;module&gt;
      import lib
  ModuleNotFoundError: No module named 'lib'
  </code></pre>
  
  <h1>Workaround</h1>
  
  <p>If I add <code>import sys; sys.path.insert(0, os.path.dirname(isi_path))</code> to <code>app.py</code>, <code>python app.py</code> yields <code>world</code> as intended, but I would like to avoid munging the <code>sys.path</code> if possible.</p>
  
  <h1>Answer requirements</h1>
  
  <p>I'd like <code>python app.py</code> to print <code>ISI says: world</code> and I'd like to accomplish this by modifying the <code>path_import</code> function.</p>
  
  <p>I'm not sure of the implications of mangling <code>sys.path</code>. Eg. if there was <code>directory/requests.py</code> and I added the path to <code>directory</code> to the <code>sys.path</code>, I wouldn't want <code>import requests</code> to start importing <code>directory/requests.py</code> instead of importing the <a href="http://docs.python-requests.org/en/master/" rel="noreferrer">requests library</a> that I installed with <code>pip install requests</code>.</p>
  
  <p>The solution <strong>MUST</strong> be implemented as a python function that accepts the absolute file path to the desired module and returns the <a href="https://docs.python.org/3.5/library/types.html#types.ModuleType" rel="noreferrer">module object</a>.</p>
  
  <p>Ideally, the solution should not introduce side-effects (eg. if it does modify <code>sys.path</code>, it should return <code>sys.path</code> to its original state). If the solution does introduce side-effects, it should explain why a solution cannot be achieved without introducing side-effects.</p>
  
  <hr>
  
  <h1><code>PYTHONPATH</code></h1>
  
  <p>If I have multiple projects doing this, I don't want to have to remember to set <code>PYTHONPATH</code> every time I switch between them. The user should just be able to <code>pip install</code> my project and run it without any additional setup.</p>
  
  <h1><code>-m</code></h1>
  
  <p>The <a href="https://docs.python.org/3/using/cmdline.html#cmdoption-m" rel="noreferrer"><code>-m</code> flag</a> is the recommended/pythonic approach, but the standard library also clearly documents <a href="https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly" rel="noreferrer">How to import source files directly</a>. I'd like to know how I can adapt that approach to cope with implicit relative imports. Clearly, Python's internals must do this, so how do the internals differ from the "import source files directly" documentation?</p>