Structuring python projects without path hacks
Asked Answered
I

2

12

I have a shared python library that I use in multiple projects, so the structure looks like this:

Project1
    main.py <--- (One of the projects that uses the library)
...
sharedlib
    __init__.py
    ps_lib.py
    another.py

Now in each project's main.py I use the following hack to make it work:

import os
import sys
sys.path.insert(0, os.path.abspath('..'))

import sharedlib.ps_lib
...

Is there a way to do it without using this hack? Or is there a better way to organize the projects structure?

Insulate answered 16/8, 2017 at 10:1 Comment(11)
Why not just "install" the sharedlib as package? Then you can import it everywhere.Presbyterian
@Rahul I don't like this approach because it's not elegant, and in every module in the project you would have "from .. import ..."Insulate
@Presbyterian but then after each small fix in the shared library, I would have to pip install it againInsulate
Not necessarily, you can install a "symlink" with something like python setup.py develop. That way if you change sharedlib the changes will take effect immediatly (or after an interpreter restart if you're using an interactive interpreter).Presbyterian
@Presbyterian That's a good idea! Will the symlink work in windows?Insulate
I never had problems with python setup.py develop and I'm using windows. However that requires you to make the sharedlib package installable (I always used setuptools.setup but it probably also works with distutils.setup). :)Presbyterian
@Presbyterian Ok so maybe this is the correct approach, you can post your answerInsulate
@Insulate You don't need to install after every edit if you run pip install -e ..Cufic
@Presbyterian I believe the develop command is only available in setuptools, but not in distutils.Firestone
@Firestone You're right. I just found "Why does setup.py develop not work?" when using pure distutils...Presbyterian
this only works if the script is run from the same directory as what it's inJerejereld
P
6

I think the best way would be to make sharedlib a real package. That means changing the structure a bit:

sharedlib/
    sharedlib/
        __init__.py
        ps_lib.py
        another.py
    setup.py

And using something like this in the setup.py (taken partially from Python-packaging "Minimal Structure"):

from setuptools import setup

setup(name='sharedlib',
      version='0.1',
      description='...',
      license='...',
      packages=['sharedlib'],   # you might need to change this if you have subfolders.
      zip_safe=False)

Then install it with python setup.py develop or pip install -e . when in the root folder of the sharedlib package.

That way (using the develop or -e option) changes to the contents of sharedlib/sharedlib/* files will be visible without re-installing the sharedlib package - although you may need to restart the interpreter if you're working in an interactive interpreter. That's because the interpreter caches already imported packages.

From the setuptools documentation:

Setuptools allows you to deploy your projects for use in a common directory or staging area, but without copying any files. Thus, you can edit each project’s code in its checkout directory, and only need to run build commands when you change a project’s C extensions or similarly compiled files. [...]

To do this, use the setup.py develop command.

(emphasis mine)

The most important thing is that you can import sharedlib everywhere now - no need to insert the sharedlib package in the PATH or PYTHONPATH anymore because Python (or at least the Python where you installed it) now treats sharedlib like any other installed package.

Presbyterian answered 16/8, 2017 at 10:42 Comment(0)
E
0

The way we do it is to use bash entry-scripts for the python scripts. Our directory structure would look similar to the following:

/opt/stackoverflow/
                 -> bin
                 -> conf
                 -> lib
                 -> log

Our lib folder then contains all of our sub-projects

/opt/stackoverflow/lib/
                    -> python_algorithms
                    -> python_data_structures
                    -> python_shared_libraries

and then when we want to execute a python script, we'll execute it via a bash script within the bin directory

/opt/stackoverflow/bin/
                    -> quick_sort.sh
                    -> merge_sort.sh

and if we cat one of our entry scripts

cat merge_sort.sh

#!/bin/bash
export STACKOVERFLOW_HOME=/opt/stackoverflow
export STACKOVERFLOW_BIN=${STACKOVERFLOW_HOME}/bin
export STACKOVERFLOW_LIB=${STACKOVERFLOW_HOME}/lib
export STACKOVERFLOW_LOG=${STACKOVERFLOW_HOME}/log
export STACKOVERFLOW_CONF=${STACKOVERFLOW_HOME}/conf

# Do any pre-script server work here

export PYTHONPATH=${PYTHONPATH}:${STACKOVERFLOW_LIB}

/usr/bin/python "${STACKOVERFLOW_LIB}/python_algorithms/merge_sort.py" $* 2>&1
Epigraphy answered 16/8, 2017 at 10:28 Comment(2)
doesn't this cause your PYTHONPATH to grow? every time you run this bash script, the path ${STACKOVERFLOW_LIB} is appended (regardless if it is part of the path already).Focal
Assuming you use the 1 user for everything and dont log out of the server it will continue to grow.Epigraphy

© 2022 - 2024 — McMap. All rights reserved.