Search code examples
pythonipythonrelative-pathpython-packaging

How do imports work in IPython


I'm a little bewildered by exactly how import statements work in IPython. I've turned up nothing through web searches.

Implicit relative imports work with Python 2, but I don't know if that's still the case with IPython for Python 3.

Relative imports using the dot syntax dont seem to work at all:

In [6]: ls 
dsp/  __init__.py  __init__.pyc  utils/

In [7]: from .utils import capture
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-e7d50007bdd1> in <module>()
----> 1 from .utils import capture

ValueError: Attempted relative import in non-package

importing modules that use the dot syntax seems impossible:

In [8]: cd utils
/home/user/workspace/mypkg/mypkg/utils

In [9]: ls
capture/  capture.py  capture.pyc  cext/  __init__.py  __init__.pyc

In [10]: from capture import Capture
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-8c31c76d052d> in <module>()
----> 1 from capture import Capture

/home/user/workspace/mypkg/mypkg/utils/capture.py in <module>()
     17 import tarfile
     18 import re
---> 19 from .. import utils
     20 from . import flprint
     21 from select import poll

ValueError: Attempted relative import in non-package

Is there some concise documentation on this somewhere?


Solution

  • The problem is I was importing the module from a lower position in the package hierarchy than is used in the module's import statement. So if I cd into the utils directory and run

    from capture import Capture
    

    then capture becomes the top level of the hierarchy. So the import statement in the capture module

    from .. import utils
    

    goes beyond the top level. Python doesn't know what ".." refers to, because modules aren't self-aware of what package they belong to. If I change back up to the mypkg directory, I get the same problem

    In [13]: cd ..
    /home/user/workspace/myproj/mypkg
    
    In [14]: from utils import capture
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-14-c87f26b2171d> in <module>()
    ----> 1 from utils import capture
    
    /home/user/workspace/myproj/mypkg/utils/capture.py in <module>()
        18 import re
        19 import zmq
    ---> 20 from .. import utils
        21 from . import flprint
        22 from select import poll
    
    ValueError: Attempted relative import beyond toplevel package
    

    In this case, utils is the top level, so

    from . import flprint
    

    will work, but

    from .. import utils
    

    won't work.

    I have to move one more directory up:

    In [19]: cd ..
    /home/user/workspace/myproj
    
    In [20]: from mypkg.utils import capture
    
    In [21]: cap = capture.Capture
    

    IPython can import packages and modules located in the current working directory, or from directories in the import path. I can add the package to the import path to be able to import from any working directory.

    In [23]: import sys
    
    In [24]: sys.path.append('/home/user/workspace/myproj')
    
    In [25]: cd
    /home/user
    
    In [26]: from mypkg.utils import capture
    

    You can use sys.path.append to make your module "self-aware" but, as pointed out by alpha_989, it can potentially lead to name collisions. However, it is a useful workaround when doing work inside the package hierarchy from an interactive terminal session.