Using setuptools to install files to arbitrary locations - python

Is there a way to install files to arbitrary locations with setuptools? I've used Data Files with setuptools before, but those are typically installed inside the package directory. I need to install a plugin file that will be located in the install directory of another application.

It seems that setuptools has purposely made it difficult to install files outside of the package directory.
I instead included the plugin files as package data and used the Entry Points feature of setuptools to expose the install/uninstall functions for the plugin files I wanted to distribute.
setup(
...
entry_points={
'console_scripts': [
'mypackage_install_plugins = mypackage:install_plugins',
'mypackage_uninstall_plugins = mypackage:uninstall_plugins',
],
}
)
I just added an additional step to the installation instructions to run the following command after installing the python package:
$> mypackage_install_plugins

The data_files attribute will allow you to specify full paths.
You could also do some shutil.copy magic in your setup.py, except don't.

Check out this answer:
Execute a Python script post install using distutils / setuptools
which shows how to add an arbitrary install script (python, shell, whatever) that runs at the end of the install. It'll run whther you use "setup.py install" directly, or a package manager like "pip install". With this, you can add any files you want, anywhere you want.
Unfortunately, I feel Brendan's pain - setuptools, not being a full package manager itself, does not handle the uninstall. Therefore, there's no way to have an uninstall hook to reverse what you did in the post-install script.

Related

Installing shared library with python package not separately

I have successfully built a Python package that uses CMake combined with pybind11 to create a shared object (.so - assuming only Linux usage at the moment) file. The implementation works but I am unable to remove this shared object file using pip uninstall .
My setup command in setup.py file looks like this taken from the pybind/cmake_example repository:
setup(
name='package',
version='0.0.1',
author='-',
author_email='-',
description='A test project using pybind11 and CMake',
long_description='',
ext_modules=[CMakeExtension('packagebindings')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
packages=setuptools.find_packages()
)
My CMakeLists.txt file has an install instruction that looks like this:
install(TARGETS packagebindings COMPONENT python LIBRARY DESTINATION ${Python_SITELIB})
To summarise, here are the files that are created when running pip install .:
path/to/site-packages/package/* - removed by pip uninstall package
path/to/site-packages/package-0.0.1.dist-info/* - removed by pip uninstall package
path/to/site-packages/packagebindings.cpython-37m-x86_64-linux-gnu.so - still present after pip uninstall package
I would like to know how make it so that running pip uninstall . removes the .so file.
If a further MRE is required, I can link to a repository.
Your CMake install target seems to place the .so directly into the python installation directory (DESTINATION ${Python_SITE_LIB}). I'm guessing this stops the .so from being registered by Python proper, so it is not removed when uninstalling. I would suggest to make CMake place the .so in a distribution directory, and then add the following option to setup():
data_files = [("installation_bin", ["distribution_bin/library.so"])]
This will let the .so be tracked by the Python package manager. The first string is a directory relative to the installation prefix. The second string is the .so file in your distribution, relative to the setup.py script.

Python3. Setuptools. Adding a local package to an assembly

There is a locally built package (eg main-0.1.tar.gz). There is another package (for example base-0.1) that requires main-0.1 as a dependency.
It is necessary that during the subsequent installation of the base-0.1 package, the main-0.1 package is also installed.
Those. You can specify only packages with PyPI in install_requires, but local adding packages to the assembly is not clear how.
You can add the package main-0.1.tag.gz to the base-0.1 archive using MANIFEST.in (include main-0.1.tag.gz). But further dependency_links, for example, does not work correctly.
How do I add a local package to the build of another package and then install it along with another package, as if it were pulled from PyPI?
You might want to look at:
PEP 440 ("File URLs")
PEP 508
import setuptools
setuptools.setup(
# [...]
install_requires = [
'main # file:///path/to/main-0.1.tar.gz'
# [...]
],
)
Alternatively (probably better actually), use some combination of pip install options:
pip install --no-index --find-links '/path/to/distributions' main base
Reference:
https://pip.pypa.io/en/stable/user_guide/#installing-from-local-packages
Found a rough solution. I don't know how much it is for Feng Shui, but it works.
Add include main-0.1.tar.gz to MANIFEST.in
In setup.py, at the end of the file (after calling setup ()), add:
if 'sdist' not in sys.argv[1]:
os.system('pip install main-0.1.tar.gz')
The condition may be different if, for example, sdist is not used for building (python setup.py sdist). The main thing is to somehow determine that this is running setup for assembly, and not for installation (pip install base-0.1.tar.gz in the future).
In this case, we copy the local dependent package into the archive of the package being built, and it is distributed, accordingly, along with it. And installed the same way.

How to upgrade/uninstall distutils packages (PyYAML) in windows OS

I am working in WIN10 , with python 2.7.15
I am try to install package, during the installation process I received the following error .
Cannot uninstall 'PyYAML'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
I try to uninstall with pip (18.1) command and I received the same error.
pip uninstall PyYAML
How I can uninstall/upgrade distutils packge in win10 OS.
Base distutils functionality doesn't leave any information about which files belong to a package -- thus it cannot be reliably uninstalled. That's what the message is telling you. Moreover, it doesn't have dependency metadata, so it can't be "upgraded" reliably, either. All those features are additions by setuptools (and some by wheel and pip itself).
This can happen if you installed the package directly from source with setup.py install if setup.py is distutils- rather than setuptools-based. Or if you installed it manually from some types of packages by copying/extracting files.
Unless the way you installed it provides an own uninstaller, you'll have to manually figure out which files belong to the package and delete them from Python directories.
Usually, these are:
site-packages\<package_name>* directories and/or
site-packages\<package_name>*.py for standalone modules
optionally, a site-packages\<package_name>.pth file
Generally, look for anything that bears the package's name on it.
If you can build the same package from source, you can use the build process to get a hint: build a binaly package that you can look into (e.g. setup.py bdist_wheel -- .whl is a ZIP archive) and see what files it has in it.

Write test-cases for python setuptools entry-points plugins

I built a python application (the "host" app) that defines a setuptools entry-point, so that it can be extended. Plugin-authors then have to add the following into their setup.py file:
setup(
# ...
entry_points = {
'myapp.plugins':
['plugin_1 = <foo.plugin.module>:<plugin-install-func>']
}
)
In order to test my setup, i have to
build a dummy wheel-package,
use pip to install it,
append new package's folder into sys.path and invoke pkg_resources.working_set.add_entry(package_dir)[*],
only then i can check the expected behavior (run TCs),
use pip to uninstall the package, and
finally remove installed package folder from sys.path,
And a separate package is needed for each test-case, if different functionality must be validated.
This whole testing-rig is rather verbose and clumsy.
Is there a more elegant way to write test-cases for setuptools entry-point plugins?
[*] Note: Installing a wheels or using pip* in develop mode with pip install -e <plugin-package> would not activate the plugin on the same interpreter on Linux; or at least not without appending afterwards the package folder in sys.path.
On Windows, the above problem exists only on develop mode.
I had a same problem and resolved by a dirty hack to build and 'pip install' the plugin package to test, into a tox's environment and run tests in that environment.
a test script does dirty hack, that is, to build and install a wheel package and run tests: https://github.com/ssato/yamllint-plugin-example/blob/master/tests/test_plugin.sh
tox configuration: https://github.com/ssato/yamllint-plugin-example/blob/master/tox.ini

Can Python setuptools install files outside dist-packages?

Is it actually possible to install data files from a sdist to arbitrary locations outside the sys.prefix directory using setuptools?
The documentation implies that it is possible to do so, using an absolute path in the data_files directory part, but in practice the leading slash seems to be ignored.
For instance with data_files=[('/opt/foo/bar', [...])], the files should be installed into the directory /opt/foo/bar. But they all end up in /usr/local/lib/python3.4/dist-packages/opt/foo/bar, which is no use to man nor beast.
I suspect that it used to work - has it been changed/broken recently?
(Using pip 8.1.1, Python 3.4, setuptools 20.9.0)
Best bet would be to override install with your own cmdclass and move the files yourself.
Ah, it's all pip's fault. Given a sdist, pip helpfully creates a wheel before installing that. But wheels can't install files outside dist-packages.
Luckily you can knock some sense into pip by chucking an error, causing it to fall back to installing the sdist you wanted in the first place.
# in setup.py
if 'bdist_wheel' in sys.argv:
raise RuntimeError("This setup.py does not support wheels")
(Thanks to Benjamin Bach)

Categories