From 528564519b1a02bd1abbf6687f619fded76fc09c Mon Sep 17 00:00:00 2001 From: Yamashita Yuu Date: Sat, 18 May 2013 11:55:35 +0900 Subject: [PATCH] import page --- Understanding-binstubs.md | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Understanding-binstubs.md diff --git a/Understanding-binstubs.md b/Understanding-binstubs.md new file mode 100644 index 0000000..a327d53 --- /dev/null +++ b/Understanding-binstubs.md @@ -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. `/bin/nosetests` (binstub generated by setuptools) +1. `/lib/python2.7/site-packages/nose/__init__.py` (original) + +The first file is a binstub created to wrap the second. setuptools put it in +`/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 `/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.