Re: Application libraries private, Distutils metadata available for console scripts and introspection
Ben Finney <ben+debian@benfinney.id.au> writes:
> How can I specify to Pybuild that an application should have its
> modules all in a private namespace, but have the Distutils metadata
> also available to `pkg_resources` queries?
I've made a real, minimal example of a Debian package of a Python
distribution, with the properties I've described.
* The ‘console_scripts’ entry point is used to specify command line
programs, ‘lorem’ and ‘ipsum’, using that Distutils feature. Those are
the correct names for the commands, so I don't want to mess with that.
* The ‘--install-lib’ option is used in ‘PYBUILD_INSTALL_ARGS’ to place
the Python packages in an application-private directory,
‘/usr/share/foo-app/’. The point of that is so they won't be on the
Python module search path.
* The ‘--install-scripts’ option is *not* used, because Distutils places
the constructed scripts in the correct directory (‘/usr/bin/’) by
default.
* The script names are correct, but are identical to names of
directories in the ‘/usr/share/foo-app/’ top level. That's
another good reason not to use the ‘--install-scripts’ option. It is
also normal and expected, and shouldn't be a problem because the
scripts will not exist there; they belong in ‘/usr/bin/’.
* The console scripts constructed by Distutils call the function
‘pkg_resources.load_entry_point’. That requires that the Distutils
metadata (the ‘FooApp-1.2.3.egg-info/’ directory) be available to that
function.
According to Robert's earlier message, that means the Distutils
metadata file needs to be not in the application's private directory,
but in a directory on the Python module search path. That seems odd to
me, since this is not amodule import being done.
What happens is that the Distutils metadata also gets hidden away in the
private directory. Then the ‘pkg_resources.load_entry_point’ function
tries to find the distribution, and can't because it's not in the Python
module search path.
So what i'm looking for is a way to tell Distutils at install time:
* Install the Python libraries to this specific private path that isn't
part of the Python module search path” (‘--install-lib’).
* Ensure the Distutils metadata can be found by these specific console
scripts (and, ideally, not by others) when they run. Either:
* Construct the console scripts, at install time, to load the
Distutils metadata at run time from that same private directory
where Distutils installed it.
Or:
* Construct the Distutils metadata, at install time, such that it
points to the private directory for these libraries. Put this
metadata where it will be found at run time.
* Install the scripts to the default, public script install location.
That facility would ideally be an option I can just add to
‘PYBUILD_INSTALL_ARGS’.
Here is the output from the example. I can provide the files if anyone
wants to experiment.
=====
$ pwd
/home/bignose/Projects/debian/foo-app-1.2.3
$ find .
.
./setup.py
./lorem
./lorem/amet.py
./lorem/dolor.py
./lorem/sit.py
./lorem/__init__.py
./ipsum
./ipsum/elit.py
./ipsum/adipiscing.py
./ipsum/consecteur.py
./ipsum/__init__.py
./debian
./debian/compat
./debian/rules
./debian/changelog
./debian/control
$ cat ./setup.py
from setuptools import (setup, find_packages)
setup(
name="FooApp",
version="1.2.3",
packages=find_packages(),
entry_points={
'console_scripts': [
"lorem = FooApp.lorem.dolor:main",
"ipsum = FooApp.ipsum.consecteur:main",
],
},
)
$ cat ./debian/rules
#! /usr/bin/make -f
export PYBUILD_INSTALL_ARGS ?= --install-lib=/usr/share/foo-app/
%:
dh $@ --with=python2 --buildsystem=pybuild
$ ./debian/rules clean
dh clean --with=python2 --buildsystem=pybuild
dh_testdir -O--buildsystem=pybuild
dh_auto_clean -O--buildsystem=pybuild
I: pybuild base:170: python2.7 setup.py clean
running clean
removing '/home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build' (and everything under it)
'build/bdist.linux-x86_64' does not exist -- can't clean it
'build/scripts-2.7' does not exist -- can't clean it
dh_clean -O--buildsystem=pybuild
$ fakeroot ./debian/rules binary
dh binary --with=python2 --buildsystem=pybuild
dh_testroot -O--buildsystem=pybuild
dh_prep -O--buildsystem=pybuild
dh_auto_install -O--buildsystem=pybuild
I: pybuild base:170: /usr/bin/python setup.py install --root /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app --install-lib=/usr/share/foo-app/
running install
running build
running build_py
running install_lib
creating /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr
creating /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share
creating /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app
creating /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/ipsum/elit.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/ipsum/adipiscing.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/ipsum/consecteur.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/ipsum/__init__.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum
creating /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/lorem/amet.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/lorem/dolor.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/lorem/sit.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem
copying /home/bignose/Projects/debian/foo-app-1.2.3/.pybuild/pythonX.Y_2.7/build/lorem/__init__.py -> /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum/elit.py to elit.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum/adipiscing.py to adipiscing.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum/consecteur.py to consecteur.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/ipsum/__init__.py to __init__.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem/amet.py to amet.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem/dolor.py to dolor.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem/sit.py to sit.pyc
byte-compiling /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/lorem/__init__.py to __init__.pyc
running install_egg_info
running egg_info
creating FooApp.egg-info
writing FooApp.egg-info/PKG-INFO
writing top-level names to FooApp.egg-info/top_level.txt
writing dependency_links to FooApp.egg-info/dependency_links.txt
writing entry points to FooApp.egg-info/entry_points.txt
writing manifest file 'FooApp.egg-info/SOURCES.txt'
reading manifest file 'FooApp.egg-info/SOURCES.txt'
writing manifest file 'FooApp.egg-info/SOURCES.txt'
Copying FooApp.egg-info to /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/share/foo-app/FooApp-1.2.3.egg-info
running install_scripts
Installing ipsum script to /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/bin
Installing lorem script to /home/bignose/Projects/debian/foo-app-1.2.3/debian/foo-app/usr/bin
dh_installdocs -O--buildsystem=pybuild
dh_installchangelogs -O--buildsystem=pybuild
dh_python2 -O--buildsystem=pybuild
dh_perl -O--buildsystem=pybuild
dh_link -O--buildsystem=pybuild
dh_compress -O--buildsystem=pybuild
dh_fixperms -O--buildsystem=pybuild
dh_installdeb -O--buildsystem=pybuild
dh_gencontrol -O--buildsystem=pybuild
dh_md5sums -O--buildsystem=pybuild
dh_builddeb -O--buildsystem=pybuild
dpkg-deb: building package 'foo-app' in '../foo-app_1.2.3-1_all.deb'.
$ sudo dpkg -i ../foo-app_1.2.3-1_all.deb
(Reading database ... 812074 files and directories currently installed.)
Preparing to unpack .../debian/foo-app_1.2.3-1_all.deb ...
Unpacking foo-app (1.2.3-1) over (1.2.3-1) ...
Setting up foo-app (1.2.3-1) ...
$ dpkg --listfiles foo-app
/.
/usr
/usr/bin
/usr/bin/ipsum
/usr/bin/lorem
/usr/share
/usr/share/doc
/usr/share/doc/foo-app
/usr/share/doc/foo-app/changelog.Debian.gz
/usr/share/python
/usr/share/python/runtime.d
/usr/share/python/runtime.d/foo-app.rtupdate
/usr/share/foo-app
/usr/share/foo-app/FooApp-1.2.3.egg-info
/usr/share/foo-app/FooApp-1.2.3.egg-info/top_level.txt
/usr/share/foo-app/FooApp-1.2.3.egg-info/dependency_links.txt
/usr/share/foo-app/FooApp-1.2.3.egg-info/entry_points.txt
/usr/share/foo-app/FooApp-1.2.3.egg-info/PKG-INFO
/usr/share/foo-app/ipsum
/usr/share/foo-app/ipsum/elit.py
/usr/share/foo-app/ipsum/adipiscing.py
/usr/share/foo-app/ipsum/consecteur.py
/usr/share/foo-app/ipsum/__init__.py
/usr/share/foo-app/lorem
/usr/share/foo-app/lorem/amet.py
/usr/share/foo-app/lorem/dolor.py
/usr/share/foo-app/lorem/sit.py
/usr/share/foo-app/lorem/__init__.py
$ cat /usr/bin/lorem
#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'FooApp==1.2.3','console_scripts','lorem'
__requires__ = 'FooApp==1.2.3'
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.exit(
load_entry_point('FooApp==1.2.3', 'console_scripts', 'lorem')()
)
$ /usr/bin/lorem
Traceback (most recent call last):
File "/usr/bin/lorem", line 5, in <module>
from pkg_resources import load_entry_point
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3084, in <module>
@_call_aside
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3070, in _call_aside
f(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3097, in _initialize_master_working_set
working_set = WorkingSet._build_master()
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 651, in _build_master
ws.require(__requires__)
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 952, in require
needed = self.resolve(parse_requirements(requirements))
File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 839, in resolve
raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'FooApp==1.2.3' distribution was not found and is required by the application
=====
--
\ “Absurdity, n. A statement or belief manifestly inconsistent |
`\ with one's own opinion.” —Ambrose Bierce, _The Devil's |
_o__) Dictionary_, 1906 |
Ben Finney
Reply to: