I splitted a complex Python script into a package for easier maintenance and distribution. I created a fresh setup.py (using setupmeta) with a console_scripts entry point and the package structure. So far, so good.
I had some unusual requirements, though:
virtualenvwrapper project,${VIRTUAL_ENV}/bin directory...${VIRTUALENVWRAPPER_PROJECT_PATH}/bin directory. (don't ask... :-)For that purpose:
I added a locate_project_path() function in the setup.py script,
added the following install_and_symlink_script subclass to setuptools.command.install.install:
class install_and_symlink_script(install):
"""Do normal install, but symlink script to project directory"""
def run(self):
install.run(self)
script_path = os.path.join(self.install_scripts, SCRIPT_NAME)
project_path = locate_project_path()
symlink_path = os.path.join(project_path, "bin", SCRIPT_NAME)
print("creating %s script symlink" % SCRIPT_NAME)
if os.path.exists(symlink_path):
print("removing existing symlink %s" % symlink_path)
os.unlink(symlink_path)
print("creating symlink from %s to %s" % (
symlink_path, script_path))
os.symlink(script_path, symlink_path)
and configured setup() this way:
setup(
...
entry_points={
"console_scripts": ["%s=myscriptpackage.cli:main" % SCRIPT_NAME],
},
cmdclass={
"install": install_and_symlink_script,
},
...
)
When performing a local python ./setup.py install, the package installation and symlink creation works perfectly.
But when performing a pip install git+ssh://.../myscriptpackage.git, it fails:
...
running install_egg_info
Copying src/myscriptpackage.egg-info to build/bdist.linux-x86_64/wheel/myscriptpackage-0.4.0-py2.7.egg-info
running install_scripts
creating my-script script symlink
creating symlink from /path/to/virtualenvwrapper/project/bin/my-script to build/bdist.linux-x86_64/wheel/myscriptpackage-0.4.0.data/scripts/my-script
error: [Errno 17] File exists
error
Failed building wheel for myscriptpackage
...
Meaning, when installing through pip instead of a python ./setup.py install:
install_and_symlink_script.install_scripts variable point to the script inside the build directory instead of the final scripts installation directory... :-|So... do you know a way to get the correct scripts installation directory, compatible with both a pip install and a python ./setup.py install ?
(Btw, I'm using python 2.7.13, setuptools 39.1.0, virtualenvwrapper 4.8.2 under Debian 9)
I knew the error: [Errno 17] File exists issue was coming from the os.path.exists(symlink_path) call.
I just understood why: if a symlink was created from a previous install, that symlink is broken during the new install. os.path.exists returns False for a broken symlink. OTOH, os.path.lexists returns True if the symlink exists, broken or not...
I found a way to consistently get the scripts installation directory when installing through a python ./setup.py install or through a pip install, using the wheel.paths.get_install_paths() function.
My setuptools custom install command is now:
...
from wheel.paths import get_install_paths
__title__ = "myscriptpackage"
...
class install_and_symlink_script(install):
"""Do normal install, but symlink script to project directory"""
def run(self):
install.run(self)
wheel_install_paths = get_install_paths(__title__)
script_path = os.path.join(wheel_install_paths['scripts'], SCRIPT_NAME)
# instead of: script_path = os.path.join(self.install_scripts, SCRIPT_NAME)
project_path = locate_project_path()
symlink_path = os.path.join(project_path, "bin", SCRIPT_NAME)
print("creating %s script symlink" % SCRIPT_NAME)
if os.path.lexists(symlink_path):
print("removing existing symlink %s" % symlink_path)
os.unlink(symlink_path)
print("creating symlink from %s to %s" % (
symlink_path, script_path))
os.symlink(script_path, symlink_path)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With