mirror of
https://github.com/pyenv/pyenv.git
synced 2025-11-16 23:33:45 -05:00
import page
69
Understanding-binstubs.md
Normal file
69
Understanding-binstubs.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
Binstubs are wrapper scripts around executables (sometimes referred to as
|
||||||
|
"binaries", although they don't have to be compiled) whose purpose is to prepare
|
||||||
|
the environment before dispatching the call to the original executable.
|
||||||
|
|
||||||
|
In the Python world, the most common binstubs are the ones that setuptools generates
|
||||||
|
after installing a gem that contains executables. But binstubs can be written in
|
||||||
|
any language, and it often makes sense to create them manually.
|
||||||
|
|
||||||
|
|
||||||
|
## setuptools
|
||||||
|
|
||||||
|
Let's see what happens when we `pip install nose`. [nose ships with an
|
||||||
|
executable][nosetests]. After the
|
||||||
|
installation, setuptools will provide us with the following executables:
|
||||||
|
|
||||||
|
1. `<python-prefix>/bin/nosetests` (binstub generated by setuptools)
|
||||||
|
1. `<python-prefix>/lib/python2.7/site-packages/nose/__init__.py` (original)
|
||||||
|
|
||||||
|
The first file is a binstub created to wrap the second. setuptools put it in
|
||||||
|
`<python-prefix>/bin` because that directory is considered to already be in our
|
||||||
|
`$PATH`.
|
||||||
|
|
||||||
|
The directory where setuptools installed the second file (the original) isn't in
|
||||||
|
our `$PATH`, but even if it was, it wouldn't be safe to run it directly because
|
||||||
|
executables in Python projects *often aren't meant* to be called directly without
|
||||||
|
any setup.
|
||||||
|
|
||||||
|
The generated binstub `<python-prefix>/bin/nosetests` is a short Python script,
|
||||||
|
presented in a slightly simplified form here:
|
||||||
|
|
||||||
|
```py
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# EASY-INSTALL-ENTRY-SCRIPT: 'nose==1.3.0','console_scripts','nosetests'
|
||||||
|
__requires__ = 'nose==1.3.0'
|
||||||
|
import sys
|
||||||
|
from pkg_resources import load_entry_point
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(
|
||||||
|
load_entry_point('nose==1.3.0', 'console_scripts', 'nosetests')()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The purpose of every setuptools binstub is to use setuptools to prepare the
|
||||||
|
`sys.path` before calling the original executable.
|
||||||
|
|
||||||
|
|
||||||
|
## pyenv
|
||||||
|
|
||||||
|
pyenv adds its own "shims" directory to `$PATH` which contains binstubs for
|
||||||
|
every executable related to Python. There are binstubs for `python`, `pip`, and for
|
||||||
|
all setuptools binstubs across each installed Python version.
|
||||||
|
|
||||||
|
When you call `rspec` on the command-line, it results in this call chain:
|
||||||
|
|
||||||
|
1. `$PYENV_ROOT/shims/rspec` (pyenv shim)
|
||||||
|
1. `$PYENV_ROOT/versions/2.7.5/bin/nosetests` (setuptools binstub)
|
||||||
|
1. `$PYENV_ROOT/versions/2.7.5/lib/python2.7/site-packages/nose/__init__.py` (original)
|
||||||
|
|
||||||
|
An pyenv shim, presented here in a slightly simplified form, is a short shell script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
export PYENV_ROOT="$HOME/.pyenv"
|
||||||
|
exec pyenv exec "$(basename "$0")" "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
The purpose of pyenv's shims is to route every call to a python executable through
|
||||||
|
`pyenv exec`, which ensures it gets executed with the right Python version.
|
||||||
Reference in New Issue
Block a user