6

I have the following directory structure:

root
  /src
    file1.py
    file2.py
  /libs
    __init__.py
    package.so

I wish to import package.so within file1.py.

I've tried the following import statements to no avail:

from .libs.package import func
from libs.package import func
from .libs import package
from libs import package

I want to avoid having to set PYTHONPATH / sys.path.

Is there a simple way to do this? I assume the issue is due to the package being a shared object and not just a Python file - I don't have access to the source code for it.

Thanks, Adam

amitchone
  • 1,444
  • 2
  • 15
  • 38
  • You can. If `package.so` is in the same directory as `file1.py`, for example, `import package` works just fine. I just wish to have the binary package in a separate directory. – amitchone Jun 22 '18 at 14:14
  • https://stackoverflow.com/questions/4383571/importing-files-from-different-folder tells me that there is no clean way to do it. – PinkFluffyUnicorn Jun 25 '18 at 11:18
  • @AdamMitchell any updates on your progress? – MoxieBall Jun 28 '18 at 16:47
  • [A nice approach](https://stackoverflow.com/questions/17211078/how-to-temporarily-modify-sys-path-in-python) to the modify-sys.path solution. – Mike C Aug 11 '19 at 02:47

3 Answers3

4

If you are intent on not using sys.path.append to import the file, I was able to do it using the following snippet in file1.py:

import imp
import os
file = os.sep+os.path.join(*os.path.realpath(__file__).split(os.sep)[:-1]+['..','libs','package.so'])
package = imp.load_source('root.libs.package', file)
print package.a()

Which prints <root.libs.package.a instance at 0x101845518>. Note that my package.so file is just a python file that defines a dummy class a so that I could test importing it. From what I have read, I believe that replacing imp.load_source with imp.load_dynamic may be what you want.

Breaking it down:

os.path.realpath(__file__).split(os.sep)[:-1] gets you the path to the directory the currently-running script is in, as a list of strings.

+['..','libs','package.so'] concatenates a list containing the parent directory (..), the libs directory, and your filename to that list, so that os.path.join will build the full path to the package.so file.

os.path.join(*[that list]) unpacks the list elements into arguments to os.path.join, and joins the strings with os.sep. I also add a leading os.sep since it is an absolute path.

imp.load_source returns a module with the name root.libs.package loaded from the file path.

This source was useful for writing this answer, and here are the docs for the imp module, which you might also find useful.

MoxieBall
  • 1,809
  • 4
  • 21
  • 1
    As much as I appreciate this answer, this method defeats the point of my question. Maybe I should have explicitly stated that I didn't just want to avoid using `sys.path.append`, I wanted a nice clean one line solution. Alas, it seems that this doesn't exist. – amitchone Jul 02 '18 at 09:08
0

You can use relative import as described in python docs:-here

Also it only works well for directories under package which will not ask for beyond import. If so will happen then this error will occur:-

valueError: attempted relative import beyond top-level package

You can refer this question too for same:- value error for relative import

Vicrobot
  • 3,306
  • 1
  • 11
  • 27
0

In order to import a file from the directory above, you should use ..

In your case, you need to import using one of the following lines:

from ..libs.package import func
from ..libs import package

from root.libs.package import func
from root.libs import package

Keep in mind that according to the python documentation, using a full path instead of .. is preferred, thus from root.libs is a better choice.

Regarding not being able to traverse up: If your main script is file1.py you should start it from the directory containing root like so:

python -m root.src.file1 args

This way python will treat your root as a package.

Last but not least: As you're using Python 2, you'll need to put __init__.py in every directory, including src and root.

Bharel
  • 12,244
  • 2
  • 27
  • 48