Skip to content

Commit 2ac789f

Browse files
authored
Automatically use requirements.in if the project uses a requirements.txt + requirements.in-setup (#641)
1 parent 714596c commit 2ac789f

File tree

16 files changed

+234
-17
lines changed

16 files changed

+234
-17
lines changed

docs/usage.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ To determine the project's dependencies, _deptry_ will scan the directory it is
3131
3. If a `pyproject.toml` file with a `[project]` section is found, _deptry_ will assume it uses [PEP 621](https://peps.python.org/pep-0621/) for dependency specification and extract:
3232
- dependencies from `[project.dependencies]` and `[project.optional-dependencies]`.
3333
- development dependencies from the groups under `[project.optional-dependencies]` passed via the [`--pep621-dev-dependency-groups`](#pep-621-dev-dependency-groups) argument.
34-
4. If a `requirements.txt` file is found, _deptry_ will extract:
35-
- dependencies from it
36-
- development dependencies from `dev-dependencies.txt` and `dependencies-dev.txt`, if any exist
34+
4. If a `requirements.in` or `requirements.txt` file is found, _deptry_ will:
35+
- extract dependencies from that file.
36+
- extract development dependencies from `dev-dependencies.txt` and `dependencies-dev.txt`, if any exist
3737

3838
_deptry_ can be configured to look for `pip` requirements files with other names or in other directories.
3939
See [Requirements files](#requirements-files) and [Requirements files dev](#requirements-files-dev).

pdm.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/deptry/cli.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
DEFAULT_EXCLUDE = ("venv", r"\.venv", r"\.direnv", "tests", r"\.git", r"setup\.py")
2828

29+
DEFAULT_REQUIREMENTS_FILES = ("requirements.txt",)
30+
2931

3032
class CommaSeparatedTupleParamType(click.ParamType):
3133
"""
@@ -159,8 +161,7 @@ def display_deptry_version(ctx: click.Context, _param: click.Parameter, value: b
159161
help=f"""A regular expression for directories or files in which .py files should not be scanned for imports to determine if there are dependency issues.
160162
Can be used multiple times by specifying the argument multiple times. re.match() is used to match the expressions, which by default checks for a match only at the beginning of a string.
161163
For example: `deptry . -e ".*/foo/" -e bar"` Note that this overwrites the defaults.
162-
[default: {", ".join(DEFAULT_EXCLUDE)}
163-
""",
164+
[default: {", ".join(DEFAULT_EXCLUDE)}]""",
164165
)
165166
@click.option(
166167
"--extend-exclude",
@@ -196,10 +197,9 @@ def display_deptry_version(ctx: click.Context, _param: click.Parameter, value: b
196197
"--requirements-files",
197198
"-rf",
198199
type=COMMA_SEPARATED_TUPLE,
199-
help=""".txt files to scan for dependencies. If a file called pyproject.toml with a [tool.poetry.dependencies] or [project] section is found, this argument is ignored
200-
and the dependencies are extracted from the pyproject.toml file instead. Can be multiple e.g. `deptry . --requirements-txt req/prod.txt,req/extra.txt`""",
201-
default=("requirements.txt",),
202-
show_default=True,
200+
help=f""".txt files to scan for dependencies. If a file called pyproject.toml with a [tool.poetry.dependencies] or [project] section is found, this argument is ignored
201+
and the dependencies are extracted from the pyproject.toml file instead. Can be multiple e.g. `deptry . --requirements-txt req/prod.txt,req/extra.txt`
202+
[default: {", ".join(DEFAULT_REQUIREMENTS_FILES)}]""",
203203
)
204204
@click.option(
205205
"--requirements-files-dev",
@@ -292,7 +292,8 @@ def deptry(
292292
ignore_notebooks=ignore_notebooks,
293293
ignore=ignore,
294294
per_rule_ignores=per_rule_ignores,
295-
requirements_files=requirements_txt or requirements_files,
295+
requirements_files=(requirements_txt or requirements_files) or DEFAULT_REQUIREMENTS_FILES,
296+
using_default_requirements_files=not (requirements_txt or requirements_files),
296297
requirements_files_dev=requirements_txt_dev or requirements_files_dev,
297298
known_first_party=known_first_party,
298299
json_output=json_output,

python/deptry/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Core:
3434
using_default_exclude: bool
3535
ignore_notebooks: bool
3636
requirements_files: tuple[str, ...]
37+
using_default_requirements_files: bool
3738
requirements_files_dev: tuple[str, ...]
3839
known_first_party: tuple[str, ...]
3940
json_output: str
@@ -48,6 +49,7 @@ def run(self) -> None:
4849
self.package_module_name_map,
4950
self.pep621_dev_dependency_groups,
5051
self.requirements_files,
52+
self.using_default_requirements_files,
5153
self.requirements_files_dev,
5254
).build()
5355

python/deptry/dependency_getter/builder.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class DependencyGetterBuilder:
3232
package_module_name_map: Mapping[str, tuple[str, ...]] = field(default_factory=dict)
3333
pep621_dev_dependency_groups: tuple[str, ...] = ()
3434
requirements_files: tuple[str, ...] = ()
35+
using_default_requirements_files: bool = True
3536
requirements_files_dev: tuple[str, ...] = ()
3637

3738
def build(self) -> DependencyGetter:
@@ -51,9 +52,10 @@ def build(self) -> DependencyGetter:
5152
self.config, self.package_module_name_map, self.pep621_dev_dependency_groups
5253
)
5354

54-
if self._project_uses_requirements_files():
55+
check, requirements_files = self._project_uses_requirements_files()
56+
if check:
5557
return RequirementsTxtDependencyGetter(
56-
self.config, self.package_module_name_map, self.requirements_files, self.requirements_files_dev
58+
self.config, self.package_module_name_map, requirements_files, self.requirements_files_dev
5759
)
5860

5961
raise DependencySpecificationNotFoundError(self.requirements_files)
@@ -114,11 +116,26 @@ def _project_uses_pep_621(pyproject_toml: dict[str, Any]) -> bool:
114116
)
115117
return False
116118

117-
def _project_uses_requirements_files(self) -> bool:
119+
def _project_uses_requirements_files(self) -> tuple[bool, tuple[str, ...]]:
120+
"""
121+
Tools like `pip-tools` and `uv` work with a setup in which a `requirements.in` is compiled into a `requirements.txt`, which then
122+
contains pinned versions for all transitive dependencies. If the user did not explicitly specify the argument `requirements-files`,
123+
but there is a `requirements.in` present, it is highly likely that the user wants to use the `requirements.in` file so we set
124+
`requirements-files` to that instead.
125+
"""
126+
if self.using_default_requirements_files and Path("requirements.in").is_file():
127+
logging.info(
128+
"Detected a 'requirements.in' file in the project and no 'requirements-files' were explicitly specified. "
129+
"Automatically using 'requirements.in' as the source for the project's dependencies. To specify a different source for "
130+
"the project's dependencies, use the '--requirements-files' option."
131+
)
132+
return True, ("requirements.in",)
133+
118134
check = any(Path(requirements_files).is_file() for requirements_files in self.requirements_files)
119135
if check:
120136
logging.debug(
121137
"Dependency specification found in '%s'. Will use this to determine the project's dependencies.\n",
122138
", ".join(self.requirements_files),
123139
)
124-
return check
140+
return True, self.requirements_files
141+
return False, ()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[tool.black]
2+
line-length = 120
3+
4+
[tool.deptry.per_rule_ignores]
5+
DEP001 = ["toml"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
black==22.6.0
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
click==8.1.3
2+
isort==5.10.1
3+
urllib3
4+
pandas
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Generated from requirements.in
2+
click==8.1.3
3+
isort==5.10.1
4+
urllib3
5+
requests
6+
pandas
7+
numpy
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from os import chdir, walk
2+
from pathlib import Path
3+
4+
import black
5+
import click
6+
import white as w
7+
from urllib3 import contrib
8+
import requests

0 commit comments

Comments
 (0)