Skip to content

Commit 6d06c3b

Browse files
authored
Sunset implicit setup.py support (#1992)
Previously if an app had only a `setup.py` file and no Python package manager files (such as `requirements.txt`, `Pipfile.lock`, `Poetry.lock` or `uv.lock`), the buildpack would install the project using `pip install --editable .` However, this implicit fallback doesn't make sense now that the buildpack supports multiple package managers, and so has to guess which package manager to use, and whether to install the project in editable mode or not. As such, in #1897 this fallback was deprecated, and is now being sunset. This also brings the classic Python buildpack's behaviour in line with the Python CNB. Apps that only have a `setup.py` file will now need to add an explicit `requirements.txt` file containing: ``` --editable . ``` GUS-W-19275444.
1 parent b9c2dc0 commit 6d06c3b

File tree

11 files changed

+57
-79
lines changed

11 files changed

+57
-79
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [Unreleased]
44

5+
- Sunset the previously deprecated support for falling back to installing dependencies from a `setup.py` file if no Python package manager files were found. ([#1992](https://github.com/heroku/heroku-buildpack-python/pull/1992))
56

67
## [v324] - 2025-12-09
78

lib/cache.sh

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,8 @@ function cache::restore() {
7474
# installed Python packages. For example, if a package entry in a requirements file is
7575
# later removed, pip will not uninstall the package. This check can be removed if we
7676
# ever switch to only caching pip's HTTP/wheel cache rather than site-packages.
77-
# TODO: Remove the `-f` check once the setup.py fallback feature is removed.
7877
# TODO: Switch this to using sha256sum like the Pipenv implementation.
79-
if [[ -f "${build_dir}/requirements.txt" ]] && ! cmp --silent "${cache_dir}/.heroku/requirements.txt" "${build_dir}/requirements.txt"; then
78+
if ! cmp --silent "${cache_dir}/.heroku/requirements.txt" "${build_dir}/requirements.txt"; then
8079
cache_invalidation_reasons+=("The contents of requirements.txt changed")
8180
fi
8281
;;
@@ -202,9 +201,7 @@ function cache::save() {
202201
# We continue to use that format so that the file can be read by older buildpack versions.
203202
echo "python-${python_full_version}" >"${cache_dir}/.heroku/python-version"
204203

205-
# TODO: Simplify this once multiple package manager files being found is turned into an
206-
# error and the setup.py fallback feature is removed.
207-
if [[ "${package_manager}" == "pip" && -f "${build_dir}/requirements.txt" ]]; then
204+
if [[ "${package_manager}" == "pip" ]]; then
208205
# TODO: Switch this to using sha256sum like the Pipenv implementation.
209206
cp "${build_dir}/requirements.txt" "${cache_dir}/.heroku/"
210207
elif [[ "${package_manager}" == "pipenv" ]]; then

lib/package_manager.sh

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,34 +59,6 @@ function package_manager::determine_package_manager() {
5959
package_managers_found_display_text+=("uv.lock (uv)")
6060
fi
6161

62-
if ((${#package_managers_found[@]} == 0)) && [[ -f "${build_dir}/setup.py" ]]; then
63-
package_managers_found+=(pip)
64-
package_managers_found_display_text+=("setup.py (pip)")
65-
output::warning <<-EOF
66-
Warning: Implicit setup.py file support is deprecated.
67-
68-
Your app currently only has a setup.py file and no Python
69-
package manager files. This means that the buildpack has
70-
to guess which package manager you want to use and also
71-
whether to install your project in editable mode or not.
72-
73-
For now, we will use pip to install your dependencies in
74-
editable mode, however, this fallback is deprecated and
75-
will be removed in the future.
76-
77-
Please add an explicit package manager file to your app.
78-
79-
To continue using pip in editable mode, create a new file
80-
in the root directory of your app named 'requirements.txt'
81-
containing the requirement '--editable .' (without quotes).
82-
83-
Alternatively, if you wish to switch to another package
84-
manager, we highly recommend uv:
85-
https://docs.astral.sh/uv/
86-
EOF
87-
build_data::set_raw "setup_py_only" "true"
88-
fi
89-
9062
local num_package_managers_found=${#package_managers_found[@]}
9163

9264
case "${num_package_managers_found}" in
@@ -95,6 +67,36 @@ function package_manager::determine_package_manager() {
9567
return 0
9668
;;
9769
0)
70+
if [[ -f "${build_dir}/setup.py" ]]; then
71+
output::error <<-EOF
72+
Error: Implicit setup.py file support has been sunset.
73+
74+
Your app currently only has a setup.py file and no Python
75+
package manager files. This means that the buildpack can't
76+
tell which package manager you want to use, and whether to
77+
install your project in editable mode or not.
78+
79+
Previously the buildpack guessed and used pip to install your
80+
dependencies in editable mode. However, this fallback was
81+
deprecated in September 2025 and has now been sunset.
82+
83+
You must now add an explicit package manager file to your app,
84+
such as a requirements.txt, poetry.lock or uv.lock file.
85+
86+
To continue using your setup.py file with pip in editable
87+
mode, create a new file in the root directory of your app
88+
named 'requirements.txt' containing the requirement
89+
'--editable .' (without quotes).
90+
91+
Alternatively, if you wish to switch to another package
92+
manager, we recommend uv, since it supports lockfiles, is
93+
faster, and is actively maintained by a full-time team:
94+
https://docs.astral.sh/uv/
95+
EOF
96+
build_data::set_string "failure_reason" "package-manager::setup-py-only"
97+
exit 1
98+
fi
99+
98100
output::error <<-EOF
99101
Error: Couldn't find any supported Python package manager files.
100102

lib/pip.sh

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,9 @@ function pip::install_dependencies() {
107107
local pip_install_command=(
108108
pip
109109
install
110+
-r requirements.txt
110111
)
111112

112-
# Support for the setup.py fallback is deprecated and will be removed in the future.
113-
if [[ -f setup.py && ! -f requirements.txt ]]; then
114-
pip_install_command+=(--editable .)
115-
else
116-
pip_install_command+=(-r requirements.txt)
117-
fi
118-
119113
# Install test dependencies too when the buildpack is invoked via `bin/test-compile` on Heroku CI.
120114
# We install both requirements files at the same time to allow pip to resolve version conflicts.
121115
if [[ -v INSTALL_TEST && -f requirements-test.txt ]]; then
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This file tests that the setup.py sunset error is not shown when other package manager files exist.

spec/fixtures/pip_basic/setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
# This tests that the setup.py fallback is not used when other package manager files exist.
2-
raise RuntimeError("setup.py should not be run!")
1+
# This file tests that the setup.py sunset error is not shown when other package manager files exist.

spec/fixtures/pipenv_basic/setup.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

spec/fixtures/poetry_basic/setup.py

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +0,0 @@
1-
from setuptools import setup
2-
3-
setup(
4-
name='test',
5-
install_requires=['six'],
6-
)

spec/hatchet/package_manager_spec.rb

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,46 +49,40 @@
4949
end
5050
end
5151

52-
# This case will be turned into an error in the future.
5352
context 'when there is only a setup.py' do
54-
let(:app) { Hatchet::Runner.new('spec/fixtures/setup_py_only') }
53+
let(:app) { Hatchet::Runner.new('spec/fixtures/setup_py_only', allow_failure: true) }
5554

56-
it 'installs packages from setup.py using pip' do
55+
it 'fails the build with an informative error message' do
5756
app.deploy do |app|
58-
expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX, Regexp::MULTILINE))
57+
expect(clean_output(app.output)).to include(<<~OUTPUT)
5958
remote: -----> Python app detected
6059
remote:
61-
remote: ! Warning: Implicit setup.py file support is deprecated.
60+
remote: ! Error: Implicit setup.py file support has been sunset.
6261
remote: !
6362
remote: ! Your app currently only has a setup.py file and no Python
64-
remote: ! package manager files. This means that the buildpack has
65-
remote: ! to guess which package manager you want to use and also
66-
remote: ! whether to install your project in editable mode or not.
63+
remote: ! package manager files. This means that the buildpack can't
64+
remote: ! tell which package manager you want to use, and whether to
65+
remote: ! install your project in editable mode or not.
6766
remote: !
68-
remote: ! For now, we will use pip to install your dependencies in
69-
remote: ! editable mode, however, this fallback is deprecated and
70-
remote: ! will be removed in the future.
67+
remote: ! Previously the buildpack guessed and used pip to install your
68+
remote: ! dependencies in editable mode. However, this fallback was
69+
remote: ! deprecated in September 2025 and has now been sunset.
7170
remote: !
72-
remote: ! Please add an explicit package manager file to your app.
71+
remote: ! You must now add an explicit package manager file to your app,
72+
remote: ! such as a requirements.txt, poetry.lock or uv.lock file.
7373
remote: !
74-
remote: ! To continue using pip in editable mode, create a new file
75-
remote: ! in the root directory of your app named 'requirements.txt'
76-
remote: ! containing the requirement '--editable .' \\(without quotes\\).
74+
remote: ! To continue using your setup.py file with pip in editable
75+
remote: ! mode, create a new file in the root directory of your app
76+
remote: ! named 'requirements.txt' containing the requirement
77+
remote: ! '--editable .' (without quotes).
7778
remote: !
7879
remote: ! Alternatively, if you wish to switch to another package
79-
remote: ! manager, we highly recommend uv:
80+
remote: ! manager, we recommend uv, since it supports lockfiles, is
81+
remote: ! faster, and is actively maintained by a full-time team:
8082
remote: ! https://docs.astral.sh/uv/
8183
remote:
82-
remote: -----> Using Python #{DEFAULT_PYTHON_MAJOR_VERSION} specified in .python-version
83-
remote: -----> Installing Python #{DEFAULT_PYTHON_FULL_VERSION}
84-
remote: -----> Installing pip #{PIP_VERSION}
85-
remote: -----> Installing dependencies using 'pip install --editable .'
86-
remote: Obtaining file:///tmp/build_.*
87-
remote: .+
88-
remote: Installing collected packages: six, test
89-
remote: Successfully installed six-.+ test-0.0.0
90-
remote: -----> Saving cache
91-
REGEX
84+
remote: ! Push rejected, failed to compile Python app.
85+
OUTPUT
9286
end
9387
end
9488
end

0 commit comments

Comments
 (0)