Skip to content

Commit

Permalink
feat: add success level, fix reading yml
Browse files Browse the repository at this point in the history
  • Loading branch information
GabDug committed Jul 3, 2023
1 parent f554ff3 commit 2bebf44
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 54 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Install it [just like any other PDM plugin](https://pdm.fming.dev/latest/dev/wri
pdm self add "sync-pre-commit-lock[pdm]""
```
Optionally, you can also specify [the plugin in your project](https://pdm.fming.dev/latest/dev/write/#specify-the-plugins-in-project) `pyproject.toml`, to share it with your team:
Optionally, you can also specify [the plugin in your project](https://pdm.fming.dev/latest/dev/write/#specify-the-plugins-in-project) `pyproject.toml`, to make it installable with `pdm install --plugins`:
```toml
[tool.pdm]
Expand Down
13 changes: 3 additions & 10 deletions src/sync_pre_commit_lock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,22 @@ class Printer(ABC):
def __init__(self, *args: Any, **kwargs: Any) -> None:
raise NotImplementedError

# self.logger = logging.getLogger("sync-pre-commit-lock")
# self.logger.setLevel(logging.DEBUG)

@abstractmethod
def debug(self, msg: str) -> None:
raise NotImplementedError

# self.logger.debug(msg)

@abstractmethod
def info(self, msg: str) -> None:
raise NotImplementedError

# self.logger.info(msg)

@abstractmethod
def warning(self, msg: str) -> None:
raise NotImplementedError

# self.logger.warning(msg)

@abstractmethod
def error(self, msg: str) -> None:
raise NotImplementedError

# self.logger.error(msg)
@abstractmethod
def success(self, msg: str) -> None:
raise NotImplementedError
28 changes: 15 additions & 13 deletions src/sync_pre_commit_lock/actions/sync_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ def data(self, value: dict[str, Any]) -> None:
@classmethod
def from_yaml_file(cls, file_path: Path) -> PreCommitHookConfig:
with file_path.open("r") as stream:
x = yaml.safe_load(stream)
if not isinstance(x, dict):
raise ValueError(f"Invalid pre-commit config file: {file_path}. Expected a dict, got {type(x)}")
if "repos" in x and not isinstance(x["repos"], list):
raise ValueError(
f"Invalid pre-commit config file: {file_path}. Expected a list for `repos`, got {type(x['repos'])}"
)
return PreCommitHookConfig(x, file_path, original_file_lines=stream.readlines())
file_contents = stream.read()

data = yaml.safe_load(file_contents)
if not isinstance(data, dict):
raise ValueError(f"Invalid pre-commit config file: {file_path}. Expected a dict, got {type(data)}")
if "repos" in data and not isinstance(data["repos"], list):
raise ValueError(
f"Invalid pre-commit config file: {file_path}. Expected a list for `repos`, got {type(data['repos'])}"
)
return PreCommitHookConfig(data, file_path, original_file_lines=file_contents.splitlines(keepends=True))

@cached_property
def repos(self) -> list[PreCommitRepo]:
Expand Down Expand Up @@ -125,14 +127,14 @@ def execute(self) -> None:
to_fix = self.analyze_repos(pre_commit_config_data.repos_normalized, mapping, mapping_reverse_by_url)

if len(to_fix) == 0:
self.printer.info("All matched pre-commit hooks already in sync with the lockfile!")
self.printer.success("All matched pre-commit hooks already in sync with the lockfile!")
return

self.printer.info("Detected pre-commit hooks that can be updated to match the lockfile:")
for repo, rev in to_fix.items():
self.printer.info(f" - {repo.repo}: {repo.rev} -> {rev}")
pre_commit_config_data.update_pre_commit_repo_versions(to_fix)
self.printer.info("Pre-commit hooks have been updated to match the lockfile!")
self.printer.success("Pre-commit hooks have been updated to match the lockfile!")

def get_pre_commit_repo_new_version(
self,
Expand All @@ -149,10 +151,10 @@ def get_pre_commit_repo_new_version(
)
formatted_rev = mapping_db_repo_info["rev"].replace("${rev}", str(locked_package.version))
if formatted_rev != pre_commit_config_repo.rev:
self.printer.warning(
self.printer.debug(
f"Pre-commit hook {pre_commit_config_repo.repo} and locked package {locked_package.name} have different versions:\n"
f" Pre-commit hook ref: {pre_commit_config_repo.rev}\n"
f" Locked package version: {locked_package.version}\n"
f" - Pre-commit hook ref: {pre_commit_config_repo.rev}\n"
f" - Locked package version: {locked_package.version}"
)
return formatted_rev
else:
Expand Down
27 changes: 18 additions & 9 deletions src/sync_pre_commit_lock/pdm_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,38 @@
from pdm.core import Core
from pdm.models.candidates import Candidate
from pdm.project import Project
from pdm.termui import UI


class PDMPrinter(Printer):
def __init__(self, project: Project, **_: Any):
self.project = project
def __init__(self, ui: UI, **_: Any):
self.ui = ui
self.plugin_prefix = "\\[sync-pre-commit-lock]"

def prefix_lines(self, msg: str) -> str:
lines = msg.split("\n")
return "\n".join(f"{self.plugin_prefix} {line}" for line in lines)

def debug(self, msg: str) -> None:
self.project.core.ui.echo(f"[info]{self.plugin_prefix} " + msg + "[/info]", verbosity=Verbosity.DEBUG)
self.ui.echo(self.prefix_lines("[debug]" + msg + "[/debug]"), verbosity=Verbosity.DEBUG)

def info(self, msg: str) -> None:
self.project.core.ui.echo(f"[info]{self.plugin_prefix} " + msg + "[/info]", verbosity=Verbosity.NORMAL)
self.ui.echo("[info]" + self.prefix_lines(msg) + "[/info]", verbosity=Verbosity.NORMAL)

def warning(self, msg: str) -> None:
self.project.core.ui.echo(f"[warning]{self.plugin_prefix} " + msg + "[/warning]", verbosity=Verbosity.NORMAL)
self.ui.echo("[warning]" + self.prefix_lines(msg) + "[/warning]", verbosity=Verbosity.NORMAL)

def error(self, msg: str) -> None:
self.project.core.ui.echo(f"[error]{self.plugin_prefix} " + msg + "[/error]", verbosity=Verbosity.NORMAL)
self.ui.echo("[error]" + self.prefix_lines(msg) + "[/error]", verbosity=Verbosity.NORMAL)

def success(self, msg: str) -> None:
self.ui.echo("[success]" + self.prefix_lines(msg) + "[/success]", verbosity=Verbosity.NORMAL)


def register_pdm_plugin(core: Core) -> None:
"""Register the plugin to PDM Core."""
pass
printer = PDMPrinter(core.ui)
printer.debug("Registered sync-pre-commit-lock plugin.")


class PDMSetupPreCommitHooks(SetupPreCommitHooks):
Expand All @@ -58,7 +67,7 @@ class PDMSyncPreCommitHooksVersion(SyncPreCommitHooksVersion):
def on_pdm_install_setup_pre_commit(
project: Project, *, hooks: HookManager, candidates: list[Candidate], dry_run: bool, **_: Any
) -> None:
printer = PDMPrinter(project)
printer = PDMPrinter(project.core.ui)
plugin_config: SyncPreCommitLockConfig = load_config()
printer.debug("Checking if pre-commit hooks are installed")

Expand All @@ -81,7 +90,7 @@ def on_pdm_lock_check_pre_commit(
project: Project, *, resolution: dict[str, Candidate], dry_run: bool, **kwargs: Any
) -> None:
plugin_config: SyncPreCommitLockConfig = load_config()
printer = PDMPrinter(project)
printer = PDMPrinter(project.core.ui)
project_root: Path = project.root

file_path = project_root / plugin_config.pre_commit_config_file
Expand Down
5 changes: 4 additions & 1 deletion src/sync_pre_commit_lock/poetry_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, io: IO) -> None:
self.plugin_prefix = "[sync-pre-commit-lock]"

def debug(self, msg: str) -> None:
self.io.write_line(f"<info>{self.plugin_prefix} {msg}</info>", verbosity=Verbosity.NORMAL)
self.io.write_line(f"<info>{self.plugin_prefix} {msg}</info>", verbosity=Verbosity.DEBUG)

def info(self, msg: str) -> None:
self.io.write_line(f"<info>{self.plugin_prefix} {msg}</info>", verbosity=Verbosity.NORMAL)
Expand All @@ -44,6 +44,9 @@ def warning(self, msg: str) -> None:
def error(self, msg: str) -> None:
return self.io.write_error_line(f"<error>{self.plugin_prefix} {msg}</error>", verbosity=Verbosity.NORMAL)

def success(self, msg: str) -> None:
return self.io.write_line(f"<success>{self.plugin_prefix} {msg}</success>", verbosity=Verbosity.NORMAL)


class PoetrySetupPreCommitHooks(SetupPreCommitHooks):
install_pre_commit_hooks_command: ClassVar[Sequence[str | bytes]] = ["poetry", "run", "pre-commit", "install"]
Expand Down
9 changes: 4 additions & 5 deletions tests/test_actions/test_pre_commit_config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ def test_data_setter_raises_not_implemented_error() -> None:
config.data = {"new": "data"}


@patch("sync_pre_commit_lock.actions.sync_hooks.yaml")
def test_from_yaml_file(mock_yaml: MagicMock) -> None:
mock_yaml.safe_load.return_value = {"repos": [{"repo": "repo1", "rev": "rev1"}]}

def test_from_yaml_file() -> None:
file_data = "repos:\n- repo: repo1\n rev: rev1\n"
mock_path = MagicMock(spec=Path)
mock_path.open = mock_open(read_data="dummy_stream")
mock_path.open = mock_open(read_data=file_data)

config = PreCommitHookConfig.from_yaml_file(mock_path)

mock_path.open.assert_called_once_with("r")
assert config.data == {"repos": [{"repo": "repo1", "rev": "rev1"}]}
assert config.pre_commit_config_file_path == mock_path
assert config.original_file_lines == file_data.splitlines(keepends=True)


@patch("sync_pre_commit_lock.actions.sync_hooks.yaml")
Expand Down
4 changes: 2 additions & 2 deletions tests/test_actions/test_sync_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def test_execute_synchronizes_hooks(
mock_build_mapping.assert_called_once()
mock_analyze_repos.assert_called_once()
pre_commit_config.update_pre_commit_repo_versions.assert_called_once_with({PreCommitRepo("repo1", "rev1"): "rev2"})
printer.info.assert_called_with("Pre-commit hooks have been updated to match the lockfile!")
printer.success.assert_called_with("Pre-commit hooks have been updated to match the lockfile!")


@patch("sync_pre_commit_lock.actions.sync_hooks.PreCommitHookConfig.from_yaml_file")
Expand Down Expand Up @@ -161,7 +161,7 @@ def test_execute_synchronizes_hooks_all_good(
mock_build_mapping.assert_called_once()
mock_analyze_repos.assert_called_once()
pre_commit_config.update_pre_commit_repo_versions.assert_not_called()
printer.info.assert_called_with("All matched pre-commit hooks already in sync with the lockfile!")
printer.success.assert_called_with("All matched pre-commit hooks already in sync with the lockfile!")


def test_get_pre_commit_repo_new_version() -> None:
Expand Down
31 changes: 20 additions & 11 deletions tests/test_pdm/test_pdm_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import Path
from unittest import mock

import pytest
from pdm.cli.hooks import HookManager
from pdm.core import Core
from pdm.models.candidates import Candidate
Expand All @@ -15,51 +16,59 @@
)

# Create the mock objects
project_mock = mock.create_autospec(Project, instance=True)


@pytest.fixture()
def project() -> Project:
x = mock.MagicMock(spec=Project)
x.root = mock.MagicMock(spec=Path)
x.core = mock.MagicMock(spec=Core)
x.core.ui = mock.MagicMock(spec=UI)
return x


hooks_mock = mock.create_autospec(HookManager, instance=True)
candidates_mock = [mock.create_autospec(Candidate, instance=True)]
config_mock = mock.create_autospec(SyncPreCommitLockConfig, instance=True)
printer_mock = mock.create_autospec(PDMPrinter, instance=True)
action_mock = mock.create_autospec(PDMSetupPreCommitHooks, instance=True)


def test_on_pdm_install_setup_pre_commit_auto_install_disabled() -> None:
def test_on_pdm_install_setup_pre_commit_auto_install_disabled(project: mock.MagicMock) -> None:
config_mock.automatically_install_hooks = False
with mock.patch("sync_pre_commit_lock.pdm_plugin.PDMPrinter", return_value=printer_mock), mock.patch(
"sync_pre_commit_lock.pdm_plugin.load_config", return_value=config_mock
):
from sync_pre_commit_lock.pdm_plugin import on_pdm_install_setup_pre_commit

on_pdm_install_setup_pre_commit(project_mock, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)
on_pdm_install_setup_pre_commit(project, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)
printer_mock.debug.assert_any_call("Automatically installing pre-commit hooks is disabled. Skipping.")


def test_on_pdm_install_setup_pre_commit_no_config_file(tmp_path: Path) -> None:
def test_on_pdm_install_setup_pre_commit_no_config_file(tmp_path: Path, project: Project) -> None:
config_mock.automatically_install_hooks = True
config_mock.pre_commit_config_file = SyncPreCommitLockConfig.pre_commit_config_file
project_mock.root = tmp_path
project.root = tmp_path
with mock.patch("sync_pre_commit_lock.pdm_plugin.PDMPrinter", return_value=printer_mock), mock.patch(
"sync_pre_commit_lock.pdm_plugin.load_config", return_value=config_mock
):
from sync_pre_commit_lock.pdm_plugin import on_pdm_install_setup_pre_commit

on_pdm_install_setup_pre_commit(project_mock, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)
on_pdm_install_setup_pre_commit(project, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)
printer_mock.info.assert_called_once_with("No pre-commit config file found, skipping pre-commit hook check")


def test_on_pdm_install_setup_pre_commit_success() -> None:
project_mock.core = mock.create_autospec(Core, instance=True)
project_mock.core.ui = mock.create_autospec(UI, instance=True)
def test_on_pdm_install_setup_pre_commit_success(project: Project) -> None:
config_mock.automatically_install_hooks = True
config_mock.pre_commit_config_file = SyncPreCommitLockConfig.pre_commit_config_file
project_mock.root = (
project.root = (
Path(__file__).parent.parent / "fixtures" / "poetry_project"
) # Assuming config file exists at this path
with mock.patch("sync_pre_commit_lock.pdm_plugin.load_config", return_value=config_mock), mock.patch(
"sync_pre_commit_lock.pdm_plugin.PDMSetupPreCommitHooks", return_value=action_mock
):
from sync_pre_commit_lock.pdm_plugin import on_pdm_install_setup_pre_commit

on_pdm_install_setup_pre_commit(project_mock, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)
on_pdm_install_setup_pre_commit(project, hooks=hooks_mock, candidates=candidates_mock, dry_run=False)

action_mock.execute.assert_called_once()
5 changes: 3 additions & 2 deletions tests/test_pdm/test_pdm_sync_pre_commit_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ def printer() -> Printer:
return x


def test_register_pdm_plugin() -> None:
core = MagicMock(spec=Core)
def test_register_pdm_plugin(project: Project) -> None:
core = project.core
register_pdm_plugin(core)
# As function has no implementation currently, nothing to assert
assert core.ui.echo.call_count == 1


@patch("sync_pre_commit_lock.pdm_plugin.load_config")
Expand Down

0 comments on commit 2bebf44

Please sign in to comment.