Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: include a third party library in a personal Python package

I would like to include a third party library into my Python script folder to distribute it all togehter (I am awary of the distribution license, and this library is fine to distribute). This is in order to avoid installing the library on another machine.

Saying I have a script (my_script.py), which calls this external library. I tried to copy this library from the site-packages subdirectory of Python directory into the directory where I have my files, but it seems not to be enough (I think th reason is in the __init__.py of this library which probably needs the folder to be in the PYTHONPATH).

Would it be reasonable to insert some lines of code in my_script.py to temporary append its folder to sys.path in order to make the all things working?

For instance, if I have a structure similar to this:

Main_folder
my_script.py
  /external_lib_folder
   __init__.py
   external_lib.py

and external_lib_folder is the external library I copied from site-packages and inserted in my Main_folder, would it be fine if I write these lines (e.g.) in my_script.py?

import os,sys
main_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(main_dir)

EDIT

I ended up choosing the sys.path.append solution. I added these lines to my my_script.py:

import os, sys

# temporarily appends the folder containing this file into sys.path
main_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'functions')
sys.path.append(main_dir)

Anyway, I chose to insert this as an edit in my question and accept the answer of Torxed because of the time he spent in helping me (and of course because his solution works as well).

like image 816
umbe1987 Avatar asked Mar 07 '16 16:03

umbe1987


1 Answers

Python3

import importlib.machinery, imp
namespace = 'external_lib'
loader = importlib.machinery.SourceFileLoader(namespace, '/home/user/external_lib_folder/external_lib.py')
external_lib = loader.load_module(namespace)

# How to use it:
external_lib.function(data_or_something)

This would be an ideal way to load custom paths in Python 3.
Not entirely sure this is what you wanted but It's relevant enough to post an alternative to adding to sys.path.

Python2

In python 2 you could just do (if i'm not mistaken, been a while since i used an older version of Python):

external_lib = __import__('external_lib_folder')

This does however require you to keep the __init__.py and a proper declaration of functions in sad script, otherwise it will fail.
**It's also important that the folder you're trying to import from is of the same name that the __init__.py script in sad folder is trying to import it's sub-libraries from, for instance geopy would be:

./myscript.py
./geopy/
./geopy/__init__.py
./geopy/compat.py
...

And the code of myscript.py would look like this:

handle = __import__('geopy')
print(handle)

Which would produce the following output:

[user@machine project]$ python2 myscript.py 
<module 'geopy' from '/home/user/project/geopy/__init__.pyc'>

[user@machine project]$ tree -L 2
.
├── geopy
│   ├── compat.py
│   ├── compat.pyc
│   ├── distance.py
│   ├── distance.pyc
│   ├── exc.py
│   ├── exc.pyc
│   ├── format.py
│   ├── format.pyc
│   ├── geocoders
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── location.py
│   ├── location.pyc
│   ├── point.py
│   ├── point.pyc
│   ├── units.py
│   ├── units.pyc
│   ├── util.py
│   ├── util.pyc
│   └── version.pyc
└── myscript.py

2 directories, 20 files

Because in __init__.py of geopy, it's defined imports such as from geopy.point import Point which requires a namespace or a folder of geopy to be present.
There for you can't rename the folder to functions and place a folder called geopy in there because that won't work, nor will placing the contents of geopy in a folder called functions because that's not what geopy will look for.

Adding the path to sys.path (Py2 + 3)

As discussed in the comments, you can also add the folder to your sys.path variable prior to imports.

import sys
sys.path.insert(0, './functions')

import geopy
print(geopy)

>>> <module 'geopy' from './functions/geopy/__init__.pyc'>

Why this is a bad idea: It will work, and is used by many. The problems that can occur is that you might replace system functions or other modules might get loaded from other folders if you're not careful where you import stuff from. There for use .insert(0, ...) for most and be sure you actually want to risk replacing system built-ins with "shady" path names.

like image 149
Torxed Avatar answered Oct 06 '22 00:10

Torxed