Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions dep_checker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from consolekit.terminal_colours import Fore, resolve_color_default
from domdf_python_tools.paths import PathPlus, in_directory
from domdf_python_tools.typing import PathLike
from shippinglabel.requirements import read_requirements
from packaging.requirements import Requirement

# this package
from dep_checker.config import AllowedUnused, ConfigReader, NameMapping, NamespacePackages
Expand Down Expand Up @@ -315,18 +315,18 @@ def iter_files_to_check(basepath: PathLike, pkg_name: str) -> Iterator[PathPlus]

def check_imports(
pkg_name: str,
req_file: PathLike = "requirements.txt",
*requirements: Requirement,
allowed_unused: Optional[List[str]] = None,
colour: Optional[bool] = None,
name_mapping: Optional[Dict[str, str]] = None,
namespace_packages: Optional[List[str]] = None,
work_dir: PathLike = '.',
) -> int:
"""
r"""
Check imports for the given package, against the given requirements file.

:param pkg_name:
:param req_file:
:param \*requirements:
:param allowed_unused: List of requirements which are allowed to be unused in the source code.
:default allowed_unused: ``[]``
:param colour: Whether to use coloured output.
Expand All @@ -346,6 +346,10 @@ def check_imports(

* Added the ``name_mapping`` option.
* Added the ``work_dir`` option.

.. versionchanged:: 1.0.0 Replaced the ``req_file`` argument with the ``*requirements`` argument.
Use :func:`shippinglabel.requirements.read_requirements(req_file)[0] <shippinglabel.requirements.read_requirements>`
to get the original bevhaviour.
"""

ret = 0
Expand All @@ -362,17 +366,11 @@ def check_imports(
namespace_packages = NamespacePackages.get(config)

work_dir = PathPlus(work_dir)
req_file = PathPlus(req_file)

if not req_file.is_absolute():
req_file = work_dir / req_file

req_file = req_file.abspath()
work_dir = work_dir.abspath()

checker = DepChecker(
pkg_name,
requirements=map(attrgetter("name"), read_requirements(req_file)[0]),
requirements=map(attrgetter("name"), requirements),
allowed_unused=allowed_unused,
name_mapping=name_mapping,
namespace_packages=namespace_packages,
Expand Down
53 changes: 45 additions & 8 deletions dep_checker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@

# stdlib
import sys
from typing import List, Optional
from typing import List, Optional, Set

# 3rd party
import click
import dom_toml
from consolekit import click_command
from consolekit.options import colour_option
from consolekit.options import colour_option, flag_option
from consolekit.utils import abort

# this package
Expand Down Expand Up @@ -61,8 +62,13 @@
"--req-file",
type=click.STRING,
metavar="FILENAME",
default="requirements.txt",
help="The requirements file.",
help="Parse the requirements from the given requirements file (or pyproject.toml file with --pyproject).",
)
@flag_option(
"-p",
"--pyproject",
help="Parse the requirements from 'pyproject.toml'.",
default=False,
)
@click.argument(
"pkg-name",
Expand All @@ -71,25 +77,56 @@
@click_command()
def main(
pkg_name: str,
req_file: str,
allowed_unused: Optional[List[str]],
colour: Optional[bool],
colour: Optional[bool] = None,
req_file: Optional[str] = None,
work_dir: str = '.',
pyproject: bool = False
) -> None:
"""
Tool to check all requirements are actually required.
"""

# 3rd party
from domdf_python_tools.paths import PathLike, PathPlus
from shippinglabel.requirements import ComparableRequirement, parse_pyproject_dependencies, read_requirements

if allowed_unused == ():
allowed_unused = None

work_dir_p = PathPlus(work_dir)

def read_req_file(req_file: PathLike) -> Set[ComparableRequirement]:
req_file = PathPlus(req_file)

if not req_file.is_absolute():
req_file = work_dir_p / req_file

return read_requirements(req_file)[0]

if pyproject:
if req_file is None:
req_file = "pyproject.toml"
dynamic = dom_toml.load(req_file)["project"].get("dynamic", ())

if "requirements" in dynamic:
requirements = read_requirements("requirements.txt")[0]
else:
requirements = parse_pyproject_dependencies(req_file, flavour="pep621")

else:
if req_file is None:
req_file = "requirements.txt"

requirements = read_req_file(req_file)

try:
ret = check_imports(
pkg_name,
req_file=req_file,
*requirements,
allowed_unused=allowed_unused,
colour=colour,
work_dir=work_dir,
work_dir=work_dir_p,
)
sys.exit(ret)
except FileNotFoundError as e:
Expand Down
6 changes: 6 additions & 0 deletions doc-source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ Command Line
:prog: dep-checker


The :option:`-P / --pyproject <-P>` option takes precedence over the :option:`--req-file` option.

.. versionchanged:: 1.0.0

Added the :option:`-P / --pyproject <-P>` option.

As a ``pre-commit`` hook
----------------------------

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ configconfig>=0.4.0
consolekit>=0.4.0
dom-toml>=0.2.0
domdf-python-tools>=3.2.0
packaging>=20.9
shippinglabel>=0.1.0
29 changes: 19 additions & 10 deletions tests/test_dep_checker.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# stdlib
from typing import Any, Dict
from typing import Any, Dict, List

# 3rd party
import pytest
from coincidence import AdvancedDataRegressionFixture
from coincidence.regressions import AdvancedFileRegressionFixture
from consolekit.testing import CliRunner, Result
from domdf_python_tools.paths import PathPlus, in_directory
from pytest_regressions.file_regression import FileRegressionFixture
from packaging.requirements import Requirement

# this package
from dep_checker import (
Expand All @@ -22,46 +23,52 @@
def test_check_imports(
single_file_project: PathPlus,
capsys,
requirements: List[str],
advanced_data_regression: AdvancedDataRegressionFixture,
):
assert check_imports("my_project", work_dir=single_file_project, colour=False) == 1
assert check_imports(
"my_project", *map(Requirement, requirements), work_dir=single_file_project, colour=False
) == 1
advanced_data_regression.check(capsys.readouterr())


def test_check_imports_package(
package_project: PathPlus,
capsys,
requirements: List[str],
advanced_data_regression: AdvancedDataRegressionFixture,
):

assert check_imports("my_project", work_dir=package_project, colour=False) == 1
assert check_imports(
"my_project", *map(Requirement, requirements), work_dir=package_project, colour=False
) == 1
advanced_data_regression.check(capsys.readouterr())


def test_cli(
single_file_project: PathPlus,
file_regression: FileRegressionFixture,
advanced_file_regression: AdvancedFileRegressionFixture,
):

with in_directory(single_file_project):
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour"])

result.check_stdout(file_regression)
result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package(package_project: PathPlus, file_regression: FileRegressionFixture):
def test_cli_package(package_project: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture):

with in_directory(package_project):
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour"])

result.check_stdout(file_regression)
result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package_srcdir(package_project: PathPlus, file_regression: FileRegressionFixture):
def test_cli_package_srcdir(package_project: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture):
(package_project / "my_project").move(package_project / "src" / "my_project")

with in_directory(package_project):
Expand All @@ -71,7 +78,7 @@ def test_cli_package_srcdir(package_project: PathPlus, file_regression: FileRegr
args=["my_project", "--no-colour", "--work-dir", "src", "--req-file", "../requirements.txt"],
)

result.check_stdout(file_regression)
result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


Expand All @@ -96,9 +103,11 @@ def test_with_config(
capsys,
advanced_data_regression: AdvancedDataRegressionFixture,
config: Dict[str, Any],
requirements: List[str],
):
assert check_imports(
"my_project",
*map(Requirement, requirements),
work_dir=single_file_project,
colour=False,
**config,
Expand Down
120 changes: 120 additions & 0 deletions tests/test_dep_checker_pyproject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# stdlib
from typing import List

# 3rd party
import dom_toml
from coincidence.regressions import AdvancedFileRegressionFixture
from consolekit.testing import CliRunner, Result
from domdf_python_tools.paths import PathPlus, in_directory

# this package
from dep_checker.__main__ import main


def test_cli(
single_file_project: PathPlus,
requirements: List[str],
advanced_file_regression: AdvancedFileRegressionFixture,
):

with in_directory(single_file_project):
dom_toml.dump(
{"project": {"name": "foo", "requirements": requirements}},
single_file_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour", "-p"])

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package(
package_project: PathPlus,
requirements: List[str],
advanced_file_regression: AdvancedFileRegressionFixture
):

with in_directory(package_project):
dom_toml.dump(
{"project": {"name": "foo", "requirements": requirements}},
package_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour", "-p"])

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package_srcdir(
package_project: PathPlus,
requirements: List[str],
advanced_file_regression: AdvancedFileRegressionFixture
):
(package_project / "my_project").move(package_project / "src" / "my_project")

with in_directory(package_project):
dom_toml.dump(
{"project": {"name": "foo", "requirements": requirements}},
package_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(
main,
args=["my_project", "--no-colour", "--work-dir", "src", "-p"],
)

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_dynamic(
single_file_project: PathPlus,
advanced_file_regression: AdvancedFileRegressionFixture,
):

with in_directory(single_file_project):
dom_toml.dump(
{"project": {"name": "foo", "dynamic": ["requirements"]}},
single_file_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour", "-p"])

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package_dynamic(package_project: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture):

with in_directory(package_project):
dom_toml.dump(
{"project": {"name": "foo", "dynamic": ["requirements"]}},
package_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(main, args=["my_project", "--no-colour", "-p"])

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1


def test_cli_package_srcdir_dynamic(
package_project: PathPlus, advanced_file_regression: AdvancedFileRegressionFixture
):
(package_project / "my_project").move(package_project / "src" / "my_project")

with in_directory(package_project):
dom_toml.dump(
{"project": {"name": "foo", "dynamic": ["requirements"]}},
package_project / "pyproject.toml",
)
runner = CliRunner()
result: Result = runner.invoke(
main,
args=["my_project", "--no-colour", "--work-dir", "src", "-p"],
)

result.check_stdout(advanced_file_regression)
assert result.exit_code == 1
Loading
Loading