Switching from "setup.py install" to pip
| Anke | Also available in: Deutsch
When working on NoN I ran into the following message:
SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools
So I took a look at it, did a quick research and decided solving the problem should be doable for me.
Interesting/important links to start with:
What has setup.py ever done for us?
Among the obvious part, installing the application itself into the correct destination folder while also handling dependencies, one can specify plenty of additional files to be coped with like starter scripts, .desktop files, app icons, providing all kinds of the project's metadata.
If you are fierce enough or just don't know better you can write custom install/de-install/ruin the system routines - anything's possible.
For my personal use case the following tasks are required:
install application files
handle dependencies
copy .desktop file
copy starter script
copy application icon
Converting setup.py to setup.cfg
You can however continue using setup.py with pip. setup.cfg is the TOML-formatted static configuration of the setuptools.setup function that is, according to the packaging tutorial, "simpler, easier to read, and avoids many common errors".
If you come from using $ python setup.py install like me you will already have a working setup.py script that can be easily transformed.
[metadata]
The first section of the file includes all the metadata so:
... import info ... with open(os.path.join(here, "README.md"), encoding="utf-8") as f: long_description = "\n" + f.read() setup( name=info.NAME, version=info.__version__, description=info.DESCRIPTION, long_description=long_description, long_description_content_type="text/markdown", license=info.__license__, ..., )
becomes
[metadata] name = non version = 0.81 description = Knights Of Ni - a GTK+ manager for your Nikola powered website long_description = file: README.md long_description_content_type = text/markdown license = MIT ...
For convenience and readability purposes I once put all the proper data into a separate info function so I didn't need to touch the install script which now has become dispensable.
[options]
The same applies for the remaining values that are listed in the options section.
All possible keywords/values are listed in the setuptools documentation. Some keys require a "section" data type that means these keys get their own subsection, p.e. "[options.data_files]".
So the old setup function
... import info ... setup( ..., python_requires=info.REQUIRES_PYTHON, packages=info.PACKAGES, package_dir=info.PACKAGE_DIR, install_requires=info.REQUIRED, include_package_data=True, data_files=info.DATAFILES, package_data=info.PACKAGE_DATA, )
becomes
[options] packages = non python_requires = >=3.6 install_requires = Nikola PyGObject PyYAML include_package_data = True [options.data_files] share/applications = data/non.desktop share/icons/hicolor/scalable/apps = non/ui/duckyou.svg bin = data/non [options.package_data] ...
build
After plagiarizing the pyproject.toml file from the packaging tutorial we are ready to build and hopefully will end up with an installable archive.
But first make sure to have a current build package builder installed:
$ python -m pip install --upgrade build
Then run build from the project's directory:
$ python -m build * Creating virtualenv isolated environment... * Installing packages in isolated environment... (setuptools>=42) * Getting dependencies for sdist... running egg_info writing non.egg-info/PKG-INFO writing dependency_links to non.egg-info/dependency_links.txt writing requirements to non.egg-info/requires.txt [...] Successfully built non-0.81.tar.gz and non-0.81-py3-none-any.whl
Install the archive with pip:
$ pip install dist/non-0.81-py3-none-any.whl
Now it's time to check if everything is in its place and working.
Minor adjustments & tidying up the project
- Start script
-
The installation path is determined by the Python version so the .desktop file points to the new start script that figures out the installation path instead of directly pointing to the application:
#!/usr/bin/env python # -*- coding: utf-8 -*- # execution file to put in /usr/bin import os import non os.chdir(os.path.dirname(non.__file__)) from non import application
- setup.py
-
None of the (custom) commands are needed anymore, the file is obsolete.
- info file
-
All information have been merged into setup.cfg, the file is obsolete.
- README.md
-
Pip now handles the installation and deinstallation process, edit installation section.
Final thoughts
Making my application pip install-ready was easier than expected with an even more compliant result than before. 10/10 would use pip again.