Repository: jazzband/pip-tools

Issue:
Add pyproject.toml as a default file name option
#### What's the problem this feature will solve?

Currently, `pyproject.toml` needs to be explicitly declared when running `pip-compile`:

```shell
> pip-compile
Usage: pip-compile [OPTIONS] [SRC_FILES]...
Try 'pip-compile -h' for help.

Error: Invalid value: If you do not specify an input file, the default is requirements.in or setup.py
>
> pip-compile pyproject.toml
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile pyproject.toml
#
anyio==3.6.2
    # via httpcore
(etc)
```

#### Describe the solution you'd like

Since `pip-compile` already supports two standard file formats (`requirements.in` and `setup.py`), it would make sense to add `pyproject.toml` to the list of automatically recognised formats.



Discussion:
Looks like `setup.cfg` would also need to be added to the list.

Resolving patch:
diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py
--- a/piptools/scripts/compile.py
+++ b/piptools/scripts/compile.py
@@ -33,7 +33,12 @@
 )
 from ..writer import OutputWriter
 
-DEFAULT_REQUIREMENTS_FILE = "requirements.in"
+DEFAULT_REQUIREMENTS_FILES = (
+    "requirements.in",
+    "setup.py",
+    "pyproject.toml",
+    "setup.cfg",
+)
 DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt"
 METADATA_FILENAMES = frozenset({"setup.py", "setup.cfg", "pyproject.toml"})
 
@@ -342,16 +347,15 @@ def cli(
     log.verbosity = verbose - quiet
 
     if len(src_files) == 0:
-        if os.path.exists(DEFAULT_REQUIREMENTS_FILE):
-            src_files = (DEFAULT_REQUIREMENTS_FILE,)
-        elif os.path.exists("setup.py"):
-            src_files = ("setup.py",)
+        for file_path in DEFAULT_REQUIREMENTS_FILES:
+            if os.path.exists(file_path):
+                src_files = (file_path,)
+                break
         else:
             raise click.BadParameter(
                 (
-                    "If you do not specify an input file, "
-                    "the default is {} or setup.py"
-                ).format(DEFAULT_REQUIREMENTS_FILE)
+                    "If you do not specify an input file, the default is one of: {}"
+                ).format(", ".join(DEFAULT_REQUIREMENTS_FILES))
             )
 
     if not output_file:


Test patch:
diff --git a/tests/test_cli_compile.py b/tests/test_cli_compile.py
--- a/tests/test_cli_compile.py
+++ b/tests/test_cli_compile.py
@@ -1393,16 +1393,6 @@ def test_stdin_without_output_file(runner):
     assert "--output-file is required if input is from stdin" in out.stderr
 
 
-def test_not_specified_input_file(runner):
-    """
-    It should raise an error if there are no input files or default input files
-    such as "setup.py" or "requirements.in".
-    """
-    out = runner.invoke(cli)
-    assert "If you do not specify an input file" in out.stderr
-    assert out.exit_code == 2
-
-
 def test_stdin(pip_conf, runner):
     """
     Test compile requirements from STDIN.
@@ -2459,6 +2449,49 @@ def test_triple_equal_pinned_dependency_is_used(
 )
 
 
+@pytest.mark.network
+@pytest.mark.parametrize(("fname", "content"), METADATA_TEST_CASES)
+def test_not_specified_input_file(
+    fake_dists, runner, make_module, fname, content, monkeypatch
+):
+    """
+    Test that a default-named file is parsed if present.
+    """
+    meta_path = make_module(fname=fname, content=content)
+    monkeypatch.chdir(os.path.dirname(meta_path))
+    out = runner.invoke(
+        cli,
+        [
+            "--output-file",
+            "-",
+            "--no-header",
+            "--no-emit-options",
+            "--no-annotate",
+            "--no-build-isolation",
+            "--find-links",
+            fake_dists,
+        ],
+    )
+    monkeypatch.undo()
+
+    assert out.exit_code == 0, out.stderr
+    assert "small-fake-a==0.1\n" == out.stdout
+
+
+def test_not_specified_input_file_without_allowed_files(runner):
+    """
+    It should raise an error if there are no input files or default input files
+    such as "setup.py" or "requirements.in".
+    """
+    out = runner.invoke(cli)
+    assert out.exit_code == 2
+    expected_error = (
+        "Error: Invalid value: If you do not specify an input file, the default "
+        "is one of: requirements.in, setup.py, pyproject.toml, setup.cfg"
+    )
+    assert expected_error in out.stderr.splitlines()
+
+
 @pytest.mark.network
 @pytest.mark.parametrize(("fname", "content"), METADATA_TEST_CASES)
 def test_input_formats(fake_dists, runner, make_module, fname, content):