Document virtualenv support for Python apps
Closes #344. Signed-off-by: Tim D. Smith <git@tim-smith.us>
This commit is contained in:
parent
134d0bb486
commit
9f6cb8ed04
@ -54,40 +54,57 @@ Formulæ for apps that require Python 3 **should** declare an unconditional depe
|
|||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
Applications should be installed to `libexec`. This prevents the app's Python modules from contaminating the system site-packages, which is important so that pip doesn't try to manage Homebrew-installed packages and because applications' Python dependencies should not be installed to an importable prefix (see below) so `import` won't work anyway.
|
Applications should be installed into a Python [virtualenv](https://virtualenv.pypa.io/en/stable/) environment rooted in `libexec`. This prevents the app's Python modules from contaminating the system site-packages and vice versa.
|
||||||
|
|
||||||
In your formula's `install` method, first set the `PYTHONPATH` environment variable to your package's libexec site-packages directory with:
|
All of the Python module dependencies of the application (and their dependencies, recursively) should be declared as `resource`s in the formula and installed into the virtualenv, as well. Each dependency should be explicitly specified; please do not rely on `setup.py` or `pip` to perform automatic dependency resolution, for the [reasons described here](Acceptable-Formulae.md#we-dont-like-install-scripts-that-download-things).
|
||||||
```ruby
|
|
||||||
ENV.prepend_create_path "PYTHONPATH", libexec/"lib/python2.7/site-packages"
|
You can use [homebrew-pypi-poet](https://pypi.python.org/pypi/homebrew-pypi-poet) to help you write resource stanzas. To use it, set up a virtualenv and install your package and all its dependencies. Then, `pip install homebrew-pypi-poet` into the same virtualenv. Running `poet some_package` will generate the necessary resource stanzas. You can do this like:
|
||||||
```
|
|
||||||
Then, use `system` with `Language::Python.setup_install_args` to invoke `setup.py` like:
|
```bash
|
||||||
```ruby
|
# Install virtualenvwrapper
|
||||||
system "python", *Language::Python.setup_install_args(libexec)
|
$ brew install python
|
||||||
|
$ python -m pip install virtualenvwrapper
|
||||||
|
$ source $(brew --prefix)/bin/virtualenvwrapper.sh
|
||||||
|
|
||||||
|
# Set up a temporary virtual environment
|
||||||
|
$ mktmpenv
|
||||||
|
|
||||||
|
# Install the package of interest as well as homebrew-pypi-poet
|
||||||
|
$ pip install some_package homebrew-pypi-poet
|
||||||
|
$ poet some_package
|
||||||
|
|
||||||
|
# Destroy the temporary virtualenv you just created
|
||||||
|
$ deactivate
|
||||||
```
|
```
|
||||||
|
|
||||||
This will have placed the scripts your Python package installs in `libexec/"bin"`, which is not symlinked into Homebrew's prefix. We need to make sure these are installed and we also need to make sure that, when they are invoked, `PYTHONPATH` includes the path where we just installed your package. Do this with:
|
Homebrew provides helper methods for instantiating and populating virtualenvs. You can use them by putting `include Language::Python::Virtualenv` on the `Formula` class definition, above `def install`.
|
||||||
|
|
||||||
|
For most applications, all you will need to write is:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
bin.install Dir[libexec/"bin/*"]
|
def install
|
||||||
bin.env_script_all_files(libexec/"bin", :PYTHONPATH => ENV["PYTHONPATH"])
|
virtualenv_install_with_resources
|
||||||
|
end
|
||||||
```
|
```
|
||||||
The first line copies all of the executables to bin. The second line writes stubs to bin that set `PYTHONPATH` and moves the original files back to `libexec/"bin"`.
|
|
||||||
|
|
||||||
## Dependencies
|
This is exactly the same as writing:
|
||||||
|
|
||||||
All Python dependencies of applications that are not packaged by Homebrew (and those dependencies' Python dependencies, recursively) **should** be unconditionally downloaded as `Resource`s and installed into the application keg's `libexec/"vendor"` path. This prevents the state of the system Python packages from being affected by installing an app with Homebrew and guarantees that apps use versions of their dependencies that are known to work together. `libexec/"vendor"` is preferred to `libexec` so that formulæ don't accidentally install executables belonging to their dependencies, which can cause linking conflicts.
|
|
||||||
|
|
||||||
Each dependency **should** be explicitly installed; please do not rely on setup.py or pip to perform automatic dependency resolution, for the [reasons described here](Acceptable-Formulae.md#we-dont-like-install-scripts-that-download-things).
|
|
||||||
|
|
||||||
You can use [homebrew-pypi-poet](https://pypi.python.org/pypi/homebrew-pypi-poet) to help you write resource stanzas. To use it, set up a virtualenv and install your package and all its dependencies. Then, `pip install homebrew-pypi-poet` into the same virtualenv. `poet -f foo` will draft a complete formula for you, or `poet foo` will just generate the resource stanzas.
|
|
||||||
|
|
||||||
Set `PYTHONPATH` to include the `libexec/"vendor"` site-packages path with:
|
|
||||||
```ruby
|
```ruby
|
||||||
ENV.prepend_create_path "PYTHONPATH", libexec/"vendor/lib/python2.7/site-packages"
|
def install
|
||||||
```
|
# Create a virtualenv in `libexec`. If your app needs Python 3, make sure that
|
||||||
before staging and installing each resourced dependency with:
|
# `depends_on :python3` is declared, and use `virtualenv_create(libexec, "python3")`.
|
||||||
```ruby
|
venv = virtualenv_create(libexec)
|
||||||
system "python", *Language::Python.setup_install_args(libexec/"vendor")
|
# Install all of the resources declared on the formula into the virtualenv.
|
||||||
|
venv.pip_install resources
|
||||||
|
# `link_scripts` takes a look at the virtualenv's bin directory before and
|
||||||
|
# after executing the block which is passed into it. If the block caused any
|
||||||
|
# new scripts to be written to the virtualenv's bin directory, link_scripts
|
||||||
|
# will symlink those scripts into the path given as its argument (here, the
|
||||||
|
# formula's `bin` directory in the Cellar.)
|
||||||
|
# `pip_install buildpath` will install the package that the formula points to,
|
||||||
|
# because buildpath is the location where the formula's tarball was unpacked.
|
||||||
|
venv.link_scripts(bin) { venv.pip_install buildpath }
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
@ -108,23 +125,27 @@ class Foo < Formula
|
|||||||
sha256 "09bfcd8f3c239c75e77b3ff05d782ab2c1aed0892f250ce2adf948d4308fe9dc"
|
sha256 "09bfcd8f3c239c75e77b3ff05d782ab2c1aed0892f250ce2adf948d4308fe9dc"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
include Language::Python::Virtualenv
|
||||||
|
|
||||||
def install
|
def install
|
||||||
ENV.prepend_create_path "PYTHONPATH", libexec/"vendor/lib/python2.7/site-packages"
|
virtualenv_install_with_resources
|
||||||
%w[six parsedatetime].each do |r|
|
|
||||||
resource(r).stage do
|
|
||||||
system "python", *Language::Python.setup_install_args(libexec/"vendor")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ENV.prepend_create_path "PYTHONPATH", libexec/"lib/python2.7/site-packages"
|
|
||||||
system "python", *Language::Python.setup_install_args(libexec)
|
|
||||||
|
|
||||||
bin.install Dir[libexec/"bin/*"]
|
|
||||||
bin.env_script_all_files(libexec/"bin", :PYTHONPATH => ENV["PYTHONPATH"])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use the more verbose form and request that specific resources are installed:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def install
|
||||||
|
venv = virtualenv_create(libexec)
|
||||||
|
%w[six parsedatetime].each do |r|
|
||||||
|
venv.pip_install resource(r)
|
||||||
|
end
|
||||||
|
venv.link_scripts(bin) { venv.pip_install buildpath }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
in case you need to do different things for different resources.
|
||||||
|
|
||||||
# Bindings
|
# Bindings
|
||||||
|
|
||||||
To add an option to a formula to build Python bindings, use `depends_on :python => :recommended` and install the bindings conditionally on `build.with? "python"` in your `install` method.
|
To add an option to a formula to build Python bindings, use `depends_on :python => :recommended` and install the bindings conditionally on `build.with? "python"` in your `install` method.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user