Repository: sbidoul/pip-deepfreeze (language: python)

Issue:
Rename requirements.txt.in to constraints.txt
`requirements.txt.in` is somewhat misnamed, as it actually contains pip constraints and options.

`constraints.txt` would more accurately reflect what the file is for, i.e. constraining dependencies when it is not practical or desired to put such constraints in `pyproject.toml` `project.dependencies`.




Discussion / hints:

Fix patch:
diff --git a/README.rst b/README.rst
index 6296613..36c1a2d 100644
--- a/README.rst
+++ b/README.rst
@@ -119,7 +119,7 @@ To update one or more dependencies to the latest allowed version, run:
 If you need to add some dependencies from VCS references (e.g. when a library
 with a patch you need is not available as a release on a package index), add
 the dependency as usual in your project, then add the VCS reference to a file
-named ``requirements.txt.in`` like this::
+named ``constraints.txt`` like this::
 
    DEPENDENCYNAME @ git+https://g.c/org/project@branch
 
@@ -129,7 +129,7 @@ reference pinned at the exact commit that was installed (you need pip version
 the same branch, simply use ``pip-df sync --update DEPENDENCYNAME``.
 
 When, later again, your branch is merged upstream and the project has published
-a release, remove the line from ``requirements.txt.in`` and run ``pip-df sync
+a release, remove the line from ``constraints.txt`` and run ``pip-df sync
 --update DEPENDENCYNAME`` to update to the latest released version.
 
 How to
@@ -173,11 +173,11 @@ Refreshing all pinned dependencies.
    ``pip-df sync --update-all``. This is equivalent to removing
    ``requirements.txt`` then running ``pip-df sync``. This is also roughly
    equivalent to reinstalling in an empty virtualenv with ``pip install -e . -c
-   requirements.txt.in`` then running ``pip freeze > requirements.txt``.
+   constraints.txt`` then running ``pip freeze > requirements.txt``.
 
 Using another package index than PyPI.
 
-   Create a file named ``requirements.txt.in`` in your project root, and add
+   Create a file named ``constraints.txt`` in your project root, and add
    pip options to it, such as ``--extra-index-url`` or ``--find-links``. You
    can add any option that `pip supports in requirements files
    <https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format>`_.
@@ -189,14 +189,14 @@ Installing dependencies from VCS.
    it. Assume for instance your project depends on the ``packaging`` library
    and you want to install a pull request you made to it. To do so, make sure
    ``packaging`` is declared as a regular dependency of your project. Then
-   add the VCS reference in ``requirements.txt.in`` like so::
+   add the VCS reference in ``constraints.txt`` like so::
 
       packaging @ git+https://github.com/you/packaging@your-branch
 
    Then run ``pip-df sync --update packaging`` to install from the branch and
    pin the exact commit in ``requirements.txt`` for reproducibility. When
    upstream merges your PR and cuts a release, you can simply remove the line
-   from ``requirements.txt.in`` and run ``pip-df sync --update packaging`` to
+   from ``constraints.txt`` and run ``pip-df sync --update packaging`` to
    refresh to the latest released version.
 
 Working with extras.
@@ -212,16 +212,16 @@ Working with extras.
 FAQ
 ---
 
-What should I put in ``requirements.txt.in``? Should I add all my dependencies
+What should I put in ``constraints.txt``? Should I add all my dependencies
 there?
 
-   ``requirements.txt.in`` is optional. The dependencies of your project must be
+   ``constraints.txt`` is optional. The dependencies of your project must be
    declared primarily in ``pyproject.toml`` (or the legacy ``setup.py/setup.cfg``).
-   ``requirements.txt.in`` may contain additional constraints if needed, such as version
+   ``constraints.txt`` may contain additional constraints if needed, such as version
    constraints on indirect dependencies that you don't control, or VCS links for
    dependencies that you need to install from VCS source.
 
-I have added a constraint in ``requirements.txt.in`` but ``pip-df sync`` does
+I have added a constraint in ``constraints.txt`` but ``pip-df sync`` does
 not honor it. What is going on?
 
    ``pip-df sync`` always gives priority to versions pinned in ``requirements.txt``,
@@ -243,7 +243,7 @@ not honor it. What is going on?
 How can I pass options to pip?
 
    The most reliable and repeatable way to pass options to pip is to add them
-   in ``requirements.txt.in``. The pip documentation lists `options that are
+   in ``constraints.txt``. The pip documentation lists `options that are
    allowed in requirements files
    <https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format>`_.
    Global options can also be set in the pip configuration file or passed via
@@ -321,7 +321,7 @@ pip-df sync
 
      Install/reinstall the project. Install/update dependencies to the latest
      allowed version according to pinned dependencies in requirements.txt or
-     constraints in requirements.txt.in. On demand update of dependencies to to
+     constraints in constraints.txt. On demand update of dependencies to to
      the latest version that matches constraints. Optionally uninstall unneeded
      dependencies.
 
diff --git a/news/59.feature b/news/59.feature
new file mode 100644
index 0000000..6c83bed
--- /dev/null
+++ b/news/59.feature
@@ -0,0 +1,2 @@
+Read constraints from ``constraints.txt`` first and fallback to ``requirements.txt.in``
+as the former name better matches the purpose of the file.
diff --git a/src/pip_deepfreeze/__main__.py b/src/pip_deepfreeze/__main__.py
index 9da64ff..60f651d 100644
--- a/src/pip_deepfreeze/__main__.py
+++ b/src/pip_deepfreeze/__main__.py
@@ -77,7 +77,7 @@ def sync(
 
     Install/reinstall the project. Install/update dependencies to the
     latest allowed version according to pinned dependencies in
-    requirements.txt or constraints in requirements.txt.in. On demand
+    requirements.txt or constraints in constraints.txt/requirements.txt.in. On demand
     update of dependencies to to the latest version that matches
     constraints. Optionally uninstall unneeded dependencies.
     """
diff --git a/src/pip_deepfreeze/sync.py b/src/pip_deepfreeze/sync.py
index fd8784c..582ebbd 100644
--- a/src/pip_deepfreeze/sync.py
+++ b/src/pip_deepfreeze/sync.py
@@ -36,6 +36,22 @@ def _req_line_sort_key(req_line: str) -> str:
     return req_name
 
 
+def _constraints_path(project_root: Path) -> Path:
+    constraints_txt = project_root / "constraints.txt"
+    if not constraints_txt.is_file():
+        # fallback to requirements.txt.in if it exists, for backward compatibility
+        requirements_txt_in = project_root / "requirements.txt.in"
+        if requirements_txt_in.is_file():
+            log_debug(
+                "Reading constraints and pip options "
+                "from 'requirements.txt.in'. "
+                "Consider renaming it to 'constraints.txt' "
+                "as this name better describes the purpose of the file."
+            )
+            return requirements_txt_in
+    return constraints_txt
+
+
 def sync(
     python: str,
     upgrade_all: bool,
@@ -48,7 +64,7 @@ def sync(
 ) -> None:
     project_name = get_project_name(python, project_root)
     project_name_with_extras = make_project_name_with_extras(project_name, extras)
-    constraints_path = project_root / "requirements.txt.in"
+    constraints_path = _constraints_path(project_root)
     # upgrade project and its dependencies, if needed
     merged_constraints_path = get_temp_path_in_dir(
         dir=project_root, prefix="requirements.", suffix=".txt.df"
@@ -83,7 +99,8 @@ def sync(
             print("# frozen requirements generated by pip-deepfreeze", file=f)
             # output pip options in main requirements only
             if not extra and constraints_path.exists():
-                # XXX can we avoid this second parse of requirements.txt.in?
+                # XXX can we avoid this second parse of
+                # constraints.txt/requirements.txt.in?
                 for parsed_req_line in parse_req_file(
                     str(constraints_path),
                     reqs_only=False,


Test patch:
diff --git a/tests/test_sync.py b/tests/test_sync.py
index 94240d2..f098c8f 100644
--- a/tests/test_sync.py
+++ b/tests/test_sync.py
@@ -11,7 +11,7 @@
 
 
 def test_sync(virtualenv_python, testpkgs, tmp_path):
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --pre
@@ -48,7 +48,7 @@ def test_sync(virtualenv_python, testpkgs, tmp_path):
 
 
 def test_sync_normalization(virtualenv_python, testpkgs, tmp_path):
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --no-index
@@ -158,7 +158,7 @@ def test_sync_editable_dep(virtualenv_python, tmp_path):
             """
         )
     )
-    constraints = tmp_path / "requirements.txt.in"
+    constraints = tmp_path / "constraints.txt"
     constraints.write_text(
         "-e git+https://github.com/PyPA/pip-test-package#egg=pip-test-package\n"
     )
@@ -189,7 +189,7 @@ def test_sync_uninstall(virtualenv_python, tmp_path, testpkgs):
         )
     )
     (tmp_path / "setup.cfg").write_text("[metadata]\nname = foobar\n")  # for perf
-    in_reqs = tmp_path / "requirements.txt.in"
+    in_reqs = tmp_path / "constraints.txt"
     in_reqs.write_text(f"--no-index\n-f {testpkgs}")
     sync(
         virtualenv_python,
@@ -261,7 +261,7 @@ def test_sync_update_new_dep(virtualenv_python, testpkgs, tmp_path):
         )
     )
     (tmp_path / "setup.cfg").write_text("[metadata]\nname = theproject\n")  # for perf
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --no-index
@@ -309,7 +309,7 @@ def test_sync_update_all_new_dep(virtualenv_python, testpkgs, tmp_path):
         )
     )
     (tmp_path / "setup.cfg").write_text("[metadata]\nname = theproject\n")  # for perf
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --no-index
@@ -344,7 +344,7 @@ def test_sync_extras(virtualenv_python, testpkgs, tmp_path):
         )
     )
     (tmp_path / "setup.cfg").write_text("[metadata]\nname = theproject\n")  # for perf
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --no-index
@@ -389,7 +389,7 @@ def test_sync_extras(virtualenv_python, testpkgs, tmp_path):
 
 
 def test_post_sync_command(virtualenv_python, testpkgs, tmp_path):
-    (tmp_path / "requirements.txt.in").write_text(
+    (tmp_path / "constraints.txt").write_text(
         textwrap.dedent(
             f"""\
             --no-index