1 |
If folks haven't looked at python_generate_wrapper_scripts in |
2 |
python.eclass, I'd suggest doing so. For examples of it's usage, grep |
3 |
for 'python_generate_wrapper_scripts' in /usr/bin/; any place you see |
4 |
it, look for <that-script-name>-${PYTHON_TARGETS} (for example, |
5 |
/usr/bin/sphinx-build{,-2.7,-3.2}. |
6 |
|
7 |
Each usage there is a separate custom script for that specific binary; |
8 |
if there is a bug in the script, well, we're screwed- requires |
9 |
re-merging the package. |
10 |
|
11 |
This setup, at least on my hardware, is .04s added to every |
12 |
invocation; this is ignoring the inode cost for each, and the issue if |
13 |
a bug ever appears in the script generation code (in which case we're |
14 |
screwed- would require re-merging the package). |
15 |
|
16 |
In parallel, we've got python-wrapper (ls /usr/bin/python -l); this is |
17 |
provided by eselect-python and basically discern what the active |
18 |
python version is, and use that in the absense of any directives. |
19 |
This is implemented in C, and is reasonably sane; the overhead for |
20 |
that is basically non-existant. |
21 |
|
22 |
Roughly, I'm proposing we do away with python eclass's |
23 |
generate_python_wrapper_scripts generation of a script, instead having |
24 |
that just symlink to a binary provided by eselect-python that handles |
25 |
this. This centralizes the implementation (fix in one spot), and |
26 |
would allow a c version to be used- basically eliminating the |
27 |
overhead. |
28 |
|
29 |
|
30 |
There's a trick to this; currently, those generated scripts hardcode |
31 |
the allowed/known python versions for that package. We obviously have |
32 |
to preserve that; I propose we shove it into the symlink path. |
33 |
|
34 |
Basically, we add a /usr/libexec/python directory; within it, we have |
35 |
a wrapper binary (explained below), and a set of symlinks pointing at |
36 |
the root of that directory. To cover our current python versions, the |
37 |
following would suffice: |
38 |
|
39 |
for x in {2.{4,5,6,7},3.{0,1,2,3,4}}-cpy 2.5-jython 2.7-pypy-1.{7,8} |
40 |
\2.7-pypy-1.9; do |
41 |
ln -s ./ /usr/libexec/python/$x |
42 |
done |
43 |
|
44 |
While that seems insane, there is a reason; via that, we can encode |
45 |
the allowed versions into the symlink. Using pkgcore's pquery for |
46 |
example (which should support cpy: 2.5, 2.6, 2.7, 3.1, 3.2, 3.3) |
47 |
instead of a wrapper script at /usr/bin/pquery, we'd have thus: |
48 |
|
49 |
targets=( 2.{5,6,7}-cpy 3.{1,2,3}-cpy ) |
50 |
targets=$(IFS=/;echo -n "${targets[*]}") |
51 |
# This results in |
52 |
# targets=2.5-cpy/2.6-cpy/2.7-cpy/3.1-cpy/3.2-cpy/3.3-cpy |
53 |
ln -s "/usr/libexec/python/${targets}/wrapper" \ |
54 |
/usr/bin/pquery |
55 |
|
56 |
/usr/libexec/python/wrapper upon invocation, takes a look at argv[0]; |
57 |
sees how it was invoked basically. This will be the /usr/bin/whatever |
58 |
pathway. It reads the symlink, in the process getting the allowed |
59 |
versions and preferred order of the versions. |
60 |
|
61 |
Few notes; vast majority of filesystems will store the symlink target |
62 |
into the core inode if at all possible- in doing so, this avoids |
63 |
wasting an inode and is only limited by the length of the target. |
64 |
That length is capped by PATH_MAX- which can range from 256 to 4k (or |
65 |
higher). |
66 |
|
67 |
For the pquery example above, that comes out to ~73 bytes for the |
68 |
symlink pathway; well under PATH_MAX. |
69 |
|
70 |
For the scenarios where PATH_MAX caps the symlink pathway, or for |
71 |
whatever reason we don't want to use that trick, a tree of files |
72 |
contained within /usr/libexec/python/ holding the allowed versions for |
73 |
the matching pathway would suffice. |
74 |
|
75 |
Either proposal here would be far faster than what we've got now; also |
76 |
will use less space (ancillary benefit). |
77 |
|
78 |
One subtle aspect here is that if we did this, it makes it possible to |
79 |
avoid losing the invocation information- currently if you did |
80 |
`/usr/bin/python3.2 $(which sphinx-build) blah`, because of how things |
81 |
are implemented now (specifically the two layers of wrappers)- you'll |
82 |
get python2.7 running that sphinx-build invocation. |
83 |
|
84 |
This is wrong (it's directly discarding what the invocation |
85 |
requested), although you're only going to see it for scripts that |
86 |
do python introspection. |
87 |
|
88 |
Via doing the restructuring I'm mentioning above, that issue can be |
89 |
fixed, while making things faster/saner. |
90 |
|
91 |
On a related note; we currently install multiple versions of the same |
92 |
script- the only difference being the shebang. If one ignores the |
93 |
shebang, in some cases this is necessary- where the script is 2to3 |
94 |
translated, and the code for py2k vs py3k differs. For most, the only |
95 |
difference is in the shebang however. |
96 |
|
97 |
While it's minor in space savings, it's possible to eliminate that |
98 |
redundancy via a shebang target that looks at the pathway it was |
99 |
invoked via. Fairly easy actually, and basically zero overhead if |
100 |
done. |
101 |
|
102 |
Either way, thoughts? |
103 |
|
104 |
What I'm proposing isn't perfect, but I'm of the view it's a step up |
105 |
from what's in place now- and via centralizing this crap, makes it |
106 |
easier to change/maintain this going forward as necessary. |
107 |
~harring |