-
-
Notifications
You must be signed in to change notification settings - Fork 614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support setup.py-less setups #1047
Comments
Notice that there are two big families of setup.py-less setups:
|
In theory, there is an unbounded number of possible build backends (the PEP term for tools that take source code and produce wheels and sdists), each of which can find build information (name, source files, dependencies, etc) in a tool-specific manner (setup.py using setuptools, setup.py compat shim from another tool, section in pyproject.toml, shell script, ini file…). Some of the recent tools already have their idea of a lock file, but pip-tools is still very useful for some others (for example, I use flit to build a wheel for an app, but pip-tools to manage its dependencies, which means the wheel is not sufficient for installing). Tools that only need to install things (e.g. tox) can use a build frontend such as Now two good things:
So you could start with custom code to help people now, add support for an official spec later, and eventually remove the custom code. (And some day there may be a standard lock file format too!) |
Stumbled over this as I was looking into this issue myself for https://github.com/uber/h3-py. Adding my vote for |
Update: PEP 631 has been accepted and is expected to be merged into PEP 621. |
The lack of this feature does penalize any projects that already followed existing PEPs recommendations regarding storing project metadata. Does anyone know an alternative tool that is able to update dependencies inside |
Somebody needs to step up and refactor pip-compile so it uses |
I tried a very quick proof of concept using diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py
index d21f0de..9dbf621 100755
--- a/piptools/scripts/compile.py
+++ b/piptools/scripts/compile.py
@@ -373,7 +373,7 @@ def cli(
constraints = []
for src_file in src_files:
is_setup_file = os.path.basename(src_file) == "setup.py"
- if is_setup_file or src_file == "-":
+ if src_file == "-":
# pip requires filenames and not files. Since we want to support
# piping from stdin, we need to briefly save the input from stdin
# to a temporary file and have pip read that. also used for
@@ -400,6 +400,10 @@ def cli(
for req in reqs:
req.comes_from = comes_from
constraints.extend(reqs)
+ elif is_setup_file:
+ from pep517 import meta
+ from pip._internal.req.constructors import install_req_from_req_string
+ constraints.extend([install_req_from_req_string(req) for req in meta.load(".").requires])
else:
constraints.extend(
parse_requirements( However,
@jaraco perhaps I'm missing something? |
You can see what
Presumably, the 'constraints' variable doesn't recognize extras when indicated as environment markers. You probably need to indicate any 'extras' (or None) when evaluating each requirement (i.e. |
Thanks @jaraco , that kind of worked, in that it produces a proper For completeness, the patch is here: diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py
index d21f0de..25cf6cf 100755
--- a/piptools/scripts/compile.py
+++ b/piptools/scripts/compile.py
@@ -8,8 +8,12 @@ from typing import Any
import click
from click import Command
from click.utils import safecall
+from pep517 import meta
from pip._internal.commands import create_command
-from pip._internal.req.constructors import install_req_from_line
+from pip._internal.req.constructors import (
+ install_req_from_line,
+ install_req_from_req_string,
+)
from pip._internal.utils.misc import redact_auth_from_url
from .._compat import parse_requirements
@@ -373,7 +377,7 @@ def cli(
constraints = []
for src_file in src_files:
is_setup_file = os.path.basename(src_file) == "setup.py"
- if is_setup_file or src_file == "-":
+ if src_file == "-":
# pip requires filenames and not files. Since we want to support
# piping from stdin, we need to briefly save the input from stdin
# to a temporary file and have pip read that. also used for
@@ -400,6 +404,13 @@ def cli(
for req in reqs:
req.comes_from = comes_from
constraints.extend(reqs)
+ elif is_setup_file:
+ constraints.extend(
+ [
+ install_req_from_req_string(req)
+ for req in meta.load(".").requires or []
+ ]
+ )
else:
constraints.extend(
parse_requirements(
@@ -421,7 +432,9 @@ def cli(
# Filter out pip environment markers which do not match (PEP496)
constraints = [
- req for req in constraints if req.markers is None or req.markers.evaluate()
+ req
+ for req in constraints
+ if req.markers is None or req.markers.evaluate(dict(extra=None))
]
log.debug("Using indexes:")
diff --git a/setup.cfg b/setup.cfg
index 5f30716..b5f2112 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -30,6 +30,7 @@ packages = find:
zip_safe = false
install_requires =
click >= 7
+ pep517
pip >= 20.1
[options.packages.find] |
That's because diff --git tests/test_cli_compile.py tests/test_cli_compile.py
index 12db0f4..7bad5f0 100644
--- tests/test_cli_compile.py
+++ tests/test_cli_compile.py
@@ -39,7 +39,16 @@ def test_command_line_overrides_pip_conf(pip_with_index_conf, runner):
assert "Using indexes:\n http://override.com" in out.stderr
-def test_command_line_setuptools_read(pip_conf, runner):
+def test_command_line_setuptools_read(runner, make_pip_conf):
+ make_pip_conf(
+ dedent(
+ f"""\
+ [global]
+ find-links = {MINIMAL_WHEELS_PATH}
+ """
+ )
+ )
+
with open("setup.py", "w") as package:
package.write(
dedent(
|
Fix jazzband#908, first step towards addressing jazzband#1047.
Fix jazzband#908, first step towards addressing jazzband#1047.
So ... what's next for PEP 621 support? I'd really like to eliminate as many ini and cfg files as I can from my repo's root and switching to a single If we really wanted to get fancy, under the assumption that |
flit is about to add support for PEP 621, and I suppose Poetry and setuptools will do the same. Not sure pip-tools can do anything related to it, since the front-end (pip) doesn't need to change anything, AFAIU. |
Perhaps pip-tools/piptools/scripts/compile.py Line 233 in 77aa19b
I’d be happy to work on a proof-of-concept PR, but I haven’t the experience with the piptools project to understand what the most piptools-like way of writing and organizing the code would be… My use case is that until other tools have better built-in support, I’d like to replace requirements.in with that of pyproject.toml in such a way that I could output a requirements file for different combinations of groups of requirements. For example: My understanding, having played around with piptools just now, is that it won’t compile without either My goal is to use piptools to create and update requirements.txt files as “lock files” that contain groups of dependencies and are checked in to version control for reproducibility and updated in future as new dependencies are added or new versions released. I’ve heard good things about using piptools for this use case, and would let me switch faster to using only pyproject.toml even if the rest of the ecosystem (such as pip or Bazel’s rules_python) only understands requirements.txt for now. The only other option I can think of would be to update a wrapper, such as https://github.com/peterdemin/pip-compile-multi to produce requirements.in files dynamically given a pyproject.toml. Such a tool might actually be useful in the short term because it could convert new toml syntax into older ini file syntax for projects that require older or dedicated config files. However, it would ultimately be a hack and while it would initially speed up adoption, it would long-term harm community use of pyproject.toml, particularly if third-party tools dislike any of the conventions around the use of toml with their app name. I suppose the alternative would be to prefix all tooling sections in the toml file with the name of the tool that would write or update backwards-compatible generated config files. Even so, my preference would be to use wrappers for each tool, such as FlakeHell for Flake8 to update them to understand pyproject.toml, under the idea that perhaps each tool would eventually receive a PR from the community to match the upstream wrapper’s functionality… I recognize this is a relatively trivial problem, adopting pyproject.toml, but the more tools that support it natively, the faster it can be adopted to simplify a project’s config files… |
I'm not sure why the last step isn't made yet but after #1311 adding support for PEP-517 is trivial one-line change; # replace this
is_setup_file = os.path.basename(src_file) == "setup.py"
# by this
is_setup_file = os.path.basename(src_file) in {"setup.py", "pyproject.toml"} With this change, if you explicitly specify UPD: I'll see what I can do. |
Any chance to tag at least a pre-release so we can start consuming the newer feature? Last tag was 6.0.1 and did not include that highly desired feature. PS. I tested with code from master and looks to work nice but I am going going to install pip-tools from master in my CI pipelines ;) |
What's the problem this feature will solve?
Due to PEP 518, projects can now have just a setup.cfg and pyproject.toml file. Pip-compile only supports setup.py when using setuptools, though.
Describe the solution you'd like
Pip-compile can use setup.cfg files to get at dependencies, like
pip-compile -U setup.cfg
.Alternative Solutions
I suppose I can copy-paste the dependencies into their own requirements.in? That would be duplication though :(
The text was updated successfully, but these errors were encountered: