mirror of
https://github.com/kennethreitz/heroku-buildpack-python.git
synced 2026-06-05 23:10:16 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 194932889d | |||
| 4293ec5564 | |||
| 57baab9f68 | |||
| a7c20eea42 | |||
| 0a4cc2c476 | |||
| cbb718bb8b | |||
| db95cdd3f3 | |||
| 04f3ddbc86 | |||
| f06f5676e8 | |||
| c8c8995d70 | |||
| 4ee3baaded | |||
| ff94908505 | |||
| 5496c02f9f | |||
| 8d6d14b671 | |||
| 98dc586a99 |
+32
-1
@@ -1,9 +1,40 @@
|
|||||||
# Python Buildpack Changelog
|
# Python Buildpack Changelog
|
||||||
|
|
||||||
|
# 110
|
||||||
|
|
||||||
|
Update Default Python to 3.6.1, bugfixes.
|
||||||
|
|
||||||
|
- Fixed automatic pip uninstall of dependencies removed from requirements.txt.
|
||||||
|
|
||||||
|
# 109
|
||||||
|
|
||||||
|
Fix output for collectstatic step.
|
||||||
|
|
||||||
|
# 108
|
||||||
|
|
||||||
|
Updated setuptools.
|
||||||
|
|
||||||
|
# 107
|
||||||
|
|
||||||
|
Bugfix for C dependency installation.
|
||||||
|
|
||||||
|
# 106
|
||||||
|
|
||||||
|
Don't install packages that could mess up packaging.
|
||||||
|
|
||||||
|
- The Python buildpack will automatically remove `six`, `pyparsing`, `appdirs`,
|
||||||
|
`setuptools`, and `distribute` from a `requirements.txt` file now, as these
|
||||||
|
packages are provided by the Python buildpack.
|
||||||
|
|
||||||
|
# 105
|
||||||
|
|
||||||
|
Improvements to output messaging.
|
||||||
|
|
||||||
# 104
|
# 104
|
||||||
|
|
||||||
unreleased
|
General improvements.
|
||||||
|
|
||||||
|
- Fix for Heroku CI.
|
||||||
- Use `pkg_resources` to check if a distribution is installed instead of
|
- Use `pkg_resources` to check if a distribution is installed instead of
|
||||||
parsing `requirements.txt`. ([#395][395])
|
parsing `requirements.txt`. ([#395][395])
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License:
|
MIT License:
|
||||||
|
|
||||||
Copyright (C) 2016 Heroku, Inc.
|
Copyright (C) 2017 Heroku, Inc.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ This is the official [Heroku buildpack](https://devcenter.heroku.com/articles/bu
|
|||||||
|
|
||||||
Recommended web frameworks include **Django** and **Flask**. The recommended webserver is **Gunicorn**. There are no restrictions around what software can be used (as long as it's pip-installable). Web processes must bind to `$PORT`, and only the HTTP protocol is permitted for incoming connections.
|
Recommended web frameworks include **Django** and **Flask**. The recommended webserver is **Gunicorn**. There are no restrictions around what software can be used (as long as it's pip-installable). Web processes must bind to `$PORT`, and only the HTTP protocol is permitted for incoming connections.
|
||||||
|
|
||||||
Some Python packages with obscure C dependencies (e.g. scipy) are [not compatible](https://devcenter.heroku.com/articles/python-c-deps).
|
Some Python packages with obscure C dependencies are [not compatible](https://devcenter.heroku.com/articles/python-c-deps).
|
||||||
|
|
||||||
See it in Action
|
See it in Action
|
||||||
----------------
|
----------------
|
||||||
@@ -23,7 +23,7 @@ Deploying a Python application couldn't be easier:
|
|||||||
$ git push heroku master
|
$ git push heroku master
|
||||||
...
|
...
|
||||||
-----> Python app detected
|
-----> Python app detected
|
||||||
-----> Installing python-2.7.13
|
-----> Installing python-3.6.2
|
||||||
$ pip install -r requirements.txt
|
$ pip install -r requirements.txt
|
||||||
Collecting requests (from -r requirements.txt (line 1))
|
Collecting requests (from -r requirements.txt (line 1))
|
||||||
Downloading requests-2.12.4-py2.py3-none-any.whl (576KB)
|
Downloading requests-2.12.4-py2.py3-none-any.whl (576KB)
|
||||||
@@ -46,11 +46,11 @@ Specify a Python Runtime
|
|||||||
Specific versions of the Python runtime can be specified with a `runtime.txt` file:
|
Specific versions of the Python runtime can be specified with a `runtime.txt` file:
|
||||||
|
|
||||||
$ cat runtime.txt
|
$ cat runtime.txt
|
||||||
python-3.6.1
|
python-2.7.13
|
||||||
|
|
||||||
Runtime options include:
|
Runtime options include:
|
||||||
|
|
||||||
|
- `python-3.6.2`
|
||||||
- `python-2.7.13`
|
- `python-2.7.13`
|
||||||
- `python-3.6.1`
|
|
||||||
- `pypy-5.7.1` (unsupported, experimental)
|
- `pypy-5.7.1` (unsupported, experimental)
|
||||||
- `pypy3-5.5.1` (unsupported, experimental)
|
- `pypy3-5.5.1` (unsupported, experimental)
|
||||||
|
|||||||
+6
-12
@@ -32,11 +32,10 @@ CACHE_DIR=$2
|
|||||||
ENV_DIR=$3
|
ENV_DIR=$3
|
||||||
|
|
||||||
# Python defaults
|
# Python defaults
|
||||||
DEFAULT_PYTHON_VERSION="python-2.7.13"
|
DEFAULT_PYTHON_VERSION="python-3.6.2"
|
||||||
DEFAULT_PYTHON_STACK="cedar-14"
|
DEFAULT_PYTHON_STACK="cedar-14"
|
||||||
PYTHON_EXE="/app/.heroku/python/bin/python"
|
PYTHON_EXE="/app/.heroku/python/bin/python"
|
||||||
PIP_VERSION="9.0.1"
|
PIP_VERSION="9.0.1"
|
||||||
SETUPTOOLS_VERSION="32.1.0"
|
|
||||||
|
|
||||||
# Common Problem Warnings
|
# Common Problem Warnings
|
||||||
export WARNINGS_LOG=$(mktemp)
|
export WARNINGS_LOG=$(mktemp)
|
||||||
@@ -145,11 +144,6 @@ let start=$(nowms)
|
|||||||
source $BIN_DIR/steps/python
|
source $BIN_DIR/steps/python
|
||||||
mtime "python.install.time" "${start}"
|
mtime "python.install.time" "${start}"
|
||||||
|
|
||||||
# Sanity check for setuptools/distribute.
|
|
||||||
let start=$(nowms)
|
|
||||||
source $BIN_DIR/steps/setuptools
|
|
||||||
mtime "setuptools.install.time" "${start}"
|
|
||||||
|
|
||||||
# Pipenv support.
|
# Pipenv support.
|
||||||
source $BIN_DIR/steps/pipenv
|
source $BIN_DIR/steps/pipenv
|
||||||
|
|
||||||
@@ -176,16 +170,16 @@ sub-env $BIN_DIR/steps/geo-libs
|
|||||||
# GDAL support.
|
# GDAL support.
|
||||||
source $BIN_DIR/steps/gdal
|
source $BIN_DIR/steps/gdal
|
||||||
|
|
||||||
# Install dependencies with Pip (where the magic happens).
|
|
||||||
let start=$(nowms)
|
|
||||||
source $BIN_DIR/steps/pip-install
|
|
||||||
mtime "pip.install.time" "${start}"
|
|
||||||
|
|
||||||
# Uninstall removed dependencies with Pip.
|
# Uninstall removed dependencies with Pip.
|
||||||
let start=$(nowms)
|
let start=$(nowms)
|
||||||
source $BIN_DIR/steps/pip-uninstall
|
source $BIN_DIR/steps/pip-uninstall
|
||||||
mtime "pip.uninstall.time" "${start}"
|
mtime "pip.uninstall.time" "${start}"
|
||||||
|
|
||||||
|
# Install dependencies with Pip (where the magic happens).
|
||||||
|
let start=$(nowms)
|
||||||
|
source $BIN_DIR/steps/pip-install
|
||||||
|
mtime "pip.install.time" "${start}"
|
||||||
|
|
||||||
# Support for NLTK corpora.
|
# Support for NLTK corpora.
|
||||||
let start=$(nowms)
|
let start=$(nowms)
|
||||||
sub-env $BIN_DIR/steps/nltk
|
sub-env $BIN_DIR/steps/nltk
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ MANAGE_FILE=${MANAGE_FILE:-fakepath}
|
|||||||
[ -f .heroku/collectstatic_disabled ] && DISABLE_COLLECTSTATIC=1
|
[ -f .heroku/collectstatic_disabled ] && DISABLE_COLLECTSTATIC=1
|
||||||
|
|
||||||
# Ensure that Django is explicitly specified in requirements.txt
|
# Ensure that Django is explicitly specified in requirements.txt
|
||||||
pip-grep -s Django && DJANGO_INSTALLED=1
|
sp-grep -s django && DJANGO_INSTALLED=1
|
||||||
|
|
||||||
|
|
||||||
if [ ! "$DISABLE_COLLECTSTATIC" ] && [ -f "$MANAGE_FILE" ] && [ "$DJANGO_INSTALLED" ]; then
|
if [ ! "$DISABLE_COLLECTSTATIC" ] && [ -f "$MANAGE_FILE" ] && [ "$DJANGO_INSTALLED" ]; then
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ PKG_CONFIG_PATH="/app/.heroku/vendor/lib/pkgconfig:$PKG_CONFIG_PATH"
|
|||||||
source $BIN_DIR/utils
|
source $BIN_DIR/utils
|
||||||
|
|
||||||
# If a package using cffi exists within requirements, use vendored libffi.
|
# If a package using cffi exists within requirements, use vendored libffi.
|
||||||
if (pip-grep -s argon2-cffi bcrypt cffi cryptography PyNaCl pyOpenSSL PyOpenSSL misaka &> /dev/null) then
|
if (pip-grep -s requirements.txt argon2-cffi bcrypt cffi cryptography django[argon2] Django[argon2] django[bcrypt] Django[bcrypt] PyNaCl pyOpenSSL PyOpenSSL requests[security] misaka &> /dev/null) then
|
||||||
|
|
||||||
if [ ! -d ".heroku/vendor/lib/libffi-3.1" ]; then
|
if [ ! -d ".heroku/vendor/lib/libffi-3.1" ]; then
|
||||||
echo "-----> Noticed cffi. Bootstrapping libffi."
|
echo "-----> Noticed cffi. Bootstrapping libffi."
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ PKG_CONFIG_PATH="/app/.heroku/vendor/lib/pkgconfig:$PKG_CONFIG_PATH"
|
|||||||
source $BIN_DIR/utils
|
source $BIN_DIR/utils
|
||||||
|
|
||||||
# If GDAL exists within requirements, use vendored gdal.
|
# If GDAL exists within requirements, use vendored gdal.
|
||||||
if (pip-grep -s GDAL pygdal &> /dev/null) then
|
if (pip-grep -s requirements.txt GDAL gdal pygdal &> /dev/null) then
|
||||||
|
|
||||||
if [ ! -f ".heroku/vendor/bin/gdalserver" ]; then
|
if [ ! -f ".heroku/vendor/bin/gdalserver" ]; then
|
||||||
echo "-----> Noticed GDAL. Bootstrapping gdal."
|
echo "-----> Noticed GDAL. Bootstrapping gdal."
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ source $BIN_DIR/utils
|
|||||||
|
|
||||||
|
|
||||||
# If pylibmc exists within requirements, use vendored libmemcached.
|
# If pylibmc exists within requirements, use vendored libmemcached.
|
||||||
if (pip-grep -s pylibmc &> /dev/null) then
|
if (pip-grep -s requirements.txt pylibmc &> /dev/null) then
|
||||||
|
|
||||||
if [ ! -d ".heroku/vendor/lib/sasl2" ]; then
|
if [ ! -d ".heroku/vendor/lib/sasl2" ]; then
|
||||||
echo "-----> Noticed pylibmc. Bootstrapping libmemcached."
|
echo "-----> Noticed pylibmc. Bootstrapping libmemcached."
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Syntax sugar.
|
|
||||||
source $BIN_DIR/utils
|
|
||||||
|
|
||||||
if (pip-grep -s setuptools distribute &> /dev/null) then
|
|
||||||
|
|
||||||
puts-warn 'The package setuptools/distribute is listed in requirements.txt.'
|
|
||||||
puts-warn 'Please remove to ensure expected behavior. '
|
|
||||||
|
|
||||||
fi
|
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build Path: /app/.heroku/python/
|
||||||
|
# Build Deps: libraries/sqlite
|
||||||
|
|
||||||
|
OUT_PREFIX=$1
|
||||||
|
|
||||||
|
echo "Building PyPy..."
|
||||||
|
SOURCE_TARBALL='https://bitbucket.org/pypy/pypy/downloads/pypy3-v5.8.0-linux64.tar.bz2'
|
||||||
|
curl -L $SOURCE_TARBALL | tar jx
|
||||||
|
cp -R pypy3-v5.8.0-linux64/* $OUT_PREFIX
|
||||||
|
|
||||||
|
ln $OUT_PREFIX/bin/pypy3 $OUT_PREFIX/bin/python
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build Path: /app/.heroku/python/
|
||||||
|
# Build Deps: libraries/sqlite
|
||||||
|
|
||||||
|
OUT_PREFIX=$1
|
||||||
|
|
||||||
|
echo "Building Python..."
|
||||||
|
SOURCE_TARBALL='https://python.org/ftp/python/3.5.3/Python-3.5.3.tgz'
|
||||||
|
curl -L $SOURCE_TARBALL | tar xz
|
||||||
|
mv Python-3.5.3 src
|
||||||
|
cd src
|
||||||
|
|
||||||
|
./configure --prefix=$OUT_PREFIX --with-ensurepip=no
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
# ln $OUT_PREFIX/bin/python3 $OUT_PREFIX/bin/python
|
||||||
|
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build Path: /app/.heroku/python/
|
||||||
|
# Build Deps: libraries/sqlite
|
||||||
|
|
||||||
|
OUT_PREFIX=$1
|
||||||
|
|
||||||
|
echo "Building Python..."
|
||||||
|
SOURCE_TARBALL='https://python.org/ftp/python/3.6.2/Python-3.6.2.tgz'
|
||||||
|
curl -L $SOURCE_TARBALL | tar xz
|
||||||
|
mv Python-3.6.2 src
|
||||||
|
cd src
|
||||||
|
|
||||||
|
./configure --prefix=$OUT_PREFIX --with-ensurepip=no
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
ln $OUT_PREFIX/bin/python3 $OUT_PREFIX/bin/python
|
||||||
|
|
||||||
+1
-2
@@ -1,2 +1 @@
|
|||||||
# TODO: Replace this once the bob-builder changes on master are released:
|
bob-builder==0.0.13
|
||||||
https://github.com/kennethreitz/bob-builder/archive/54211376a8fb49c67ecbd6798bd45b55f4d125f4.zip
|
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
python-2.7.13
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
pylibmc
|
||||||
@@ -44,6 +44,12 @@ testPsycopg2() {
|
|||||||
assertCapturedSuccess
|
assertCapturedSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testPylibmc() {
|
||||||
|
compile "pylibmc"
|
||||||
|
assertCaptured "pylibmc"
|
||||||
|
assertCapturedSuccess
|
||||||
|
}
|
||||||
|
|
||||||
testPython2() {
|
testPython2() {
|
||||||
compile "python2"
|
compile "python2"
|
||||||
assertCaptured "python-2.7.13"
|
assertCaptured "python-2.7.13"
|
||||||
@@ -56,6 +62,16 @@ testPython3() {
|
|||||||
assertCapturedSuccess
|
assertCapturedSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testSmartRequirements() {
|
||||||
|
local cache_dir="$(mktmpdir)"
|
||||||
|
compile "requirements-standard" "$cache_dir"
|
||||||
|
assertFile "requests" ".heroku/python/requirements-declared.txt"
|
||||||
|
assertCapturedSuccess
|
||||||
|
compile "psycopg2" "$cache_dir"
|
||||||
|
assertCaptured "Uninstalling requests"
|
||||||
|
assertFile "psycopg2" ".heroku/python/requirements-declared.txt"
|
||||||
|
assertCapturedSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Vendored
+46
-11
@@ -2,26 +2,58 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Usage:
|
"""Usage:
|
||||||
pip-grep [-s] <package>...
|
pip-grep [-s] <reqfile> <package>...
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
from pkg_resources import DistributionNotFound, get_distribution
|
from pip.req import parse_requirements
|
||||||
|
from pip.index import PackageFinder
|
||||||
|
from pip._vendor.requests import session
|
||||||
|
|
||||||
|
requests = session()
|
||||||
|
|
||||||
|
|
||||||
def has_any_distribution(names, silent=False):
|
class Requirements(object):
|
||||||
for name in names:
|
def __init__(self, reqfile=None):
|
||||||
try:
|
super(Requirements, self).__init__()
|
||||||
get_distribution(name)
|
self.path = reqfile
|
||||||
except DistributionNotFound:
|
self.requirements = []
|
||||||
continue
|
|
||||||
|
|
||||||
|
if reqfile:
|
||||||
|
self.load(reqfile)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Requirements \'{}\'>'.format(self.path)
|
||||||
|
|
||||||
|
def load(self, reqfile):
|
||||||
|
if not os.path.exists(reqfile):
|
||||||
|
raise ValueError('The given requirements file does not exist.')
|
||||||
|
|
||||||
|
finder = PackageFinder([], [], session=requests)
|
||||||
|
for requirement in parse_requirements(reqfile, finder=finder, session=requests):
|
||||||
|
if requirement.req:
|
||||||
|
if not getattr(requirement.req, 'name', None):
|
||||||
|
# Prior to pip 8.1.2 the attribute `name` did not exist.
|
||||||
|
requirement.req.name = requirement.req.project_name
|
||||||
|
self.requirements.append(requirement.req)
|
||||||
|
|
||||||
|
|
||||||
|
def grep(reqfile, packages, silent=False):
|
||||||
|
try:
|
||||||
|
r = Requirements(reqfile)
|
||||||
|
except ValueError:
|
||||||
if not silent:
|
if not silent:
|
||||||
print('Package {name} found!'.format(name=name))
|
print('There was a problem loading the given requirement file.')
|
||||||
|
exit(os.EX_NOINPUT)
|
||||||
|
|
||||||
exit(0)
|
for req in r.requirements:
|
||||||
|
if req.name in packages:
|
||||||
|
if not silent:
|
||||||
|
print('Package {} found!'.format(req.name))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
if not silent:
|
if not silent:
|
||||||
print('Not found.')
|
print('Not found.')
|
||||||
@@ -31,7 +63,10 @@ def has_any_distribution(names, silent=False):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = docopt(__doc__, version='pip-grep')
|
args = docopt(__doc__, version='pip-grep')
|
||||||
has_any_distribution(names=args['<package>'], silent=args['-s'])
|
|
||||||
|
kwargs = {'reqfile': args['<reqfile>'], 'packages': args['<package>'], 'silent': args['-s']}
|
||||||
|
|
||||||
|
grep(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Usage:
|
||||||
|
sp-grep [-s] <package>...
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h --help Show this screen.
|
||||||
|
"""
|
||||||
|
from docopt import docopt
|
||||||
|
from pkg_resources import DistributionNotFound, get_distribution
|
||||||
|
|
||||||
|
|
||||||
|
def has_any_distribution(names, silent=False):
|
||||||
|
for name in names:
|
||||||
|
try:
|
||||||
|
get_distribution(name)
|
||||||
|
except DistributionNotFound:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not silent:
|
||||||
|
print('Package {name} found!'.format(name=name))
|
||||||
|
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
if not silent:
|
||||||
|
print('Not found.')
|
||||||
|
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = docopt(__doc__, version='sp-grep')
|
||||||
|
has_any_distribution(names=args['<package>'], silent=args['-s'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user