Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pydocstringformatter/formatting/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__all__ = ["FORMATTERS"]
__all__ = ["FORMATTERS", "Formatter"]

from typing import List

Expand Down
10 changes: 10 additions & 0 deletions pydocstringformatter/formatting/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ def name(self) -> str:
user-facing and should be chosen carefully.
"""

@property
def activate_option(self) -> str:
"""The argparse option to activate this formatter."""
return f"--{self.name}"

@property
def deactivate_option(self) -> str:
"""The argparse option to deactivate this formatter."""
return f"--no-{self.name}"

@abc.abstractmethod
def treat_token(self, tokeninfo: tokenize.TokenInfo) -> tokenize.TokenInfo:
"""Return a modified token."""
Expand Down
78 changes: 78 additions & 0 deletions pydocstringformatter/testutils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import contextlib
import logging
from pathlib import Path
from types import TracebackType
from typing import List, Optional, Type

import pytest

from pydocstringformatter import run_docstring_formatter
from pydocstringformatter.formatting import Formatter

LOGGER = logging.getLogger(__name__)


class FormatterAsserter(contextlib.AbstractContextManager): # type: ignore[type-arg]
"""ContextManager used to assert that a Formatter does something on a docstring.

Also permit to check that nothing happens if it's deactivated.
"""

def __init__(
self,
docstring: str,
formatters: List[Formatter],
capsys: pytest.CaptureFixture[str],
tmp_path: Path,
) -> None:
self.formatters = formatters
file_name = "_".join([f.name for f in self.formatters])
self.file_to_format = tmp_path / f"test_{file_name}.py"
self.file_to_format.write_text(docstring)
self.capsys = capsys
names = [f"'{f.name}'" for f in formatters]
verb = "is" if len(names) == 1 else "are"
self.assert_msg = f"""
{{}} was modified but {', '.join(names)} {verb} {{}}.
Temp file is '{self.file_to_format}'
"""

def __enter__(self) -> "FormatterAsserter":
return self

@staticmethod
def __launch(commands: List[str]) -> None:
"""Launch pydocstringformatter while logging for easier debugging."""
run_docstring_formatter(commands)
LOGGER.info("Launching 'pydocstringformatter' with: %s", commands)

def assert_format_when_activated(self) -> None:
"""Assert that the formatter does something when activated."""
msg = self.assert_msg.format("Nothing", "activated")
self.__launch(
[str(self.file_to_format)] + [f.activate_option for f in self.formatters]
)
out, err = self.capsys.readouterr()
assert not err
assert "Nothing to do!" not in out, msg
expected = ["---", "@@", "+++"]
assert all(e in out for e in expected), msg

def assert_no_change_when_deactivated(self) -> None:
"""Assert that the formatter does nothing when deactivated."""
self.__launch(
[str(self.file_to_format)] + [f.deactivate_option for f in self.formatters]
)
out, err = self.capsys.readouterr()
assert not err
assert "Nothing to do!" in out, self.assert_msg.format(
"Something", "deactivated"
)

def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> None:
return None
34 changes: 8 additions & 26 deletions tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import os
import sys
from pathlib import Path
from typing import List

import pytest

import pydocstringformatter
from pydocstringformatter.formatting import FORMATTERS
from pydocstringformatter.testutils import FormatterAsserter


def test_no_arguments(capsys: pytest.CaptureFixture[str]) -> None:
Expand Down Expand Up @@ -116,30 +116,12 @@ def test_output_message_two_files(
assert not output.err


@pytest.mark.parametrize(
"args,should_format",
[
[[f"--no-{f.name}" for f in FORMATTERS], False],
[[f"--{f.name}" for f in FORMATTERS], True],
],
)
def test_optional_formatters(
args: List[str],
should_format: bool,
capsys: pytest.CaptureFixture[str],
tmp_path: Path,
def test_begin_quote_formatters(
capsys: pytest.CaptureFixture[str], tmp_path: Path
) -> None:
"""Test that (optional) formatters are activated or not depending on options."""
bad_docstring = tmp_path / "bad_docstring.py"
bad_docstring.write_text(f'"""{"a" * 120}\n{"b" * 120}"""')
pydocstringformatter.run_docstring_formatter([str(bad_docstring)] + args)
out, err = capsys.readouterr()
assert not err
if should_format:
msg = "Nothing was modified, but all formatters are activated."
assert "Nothing to do!" not in out
expected = ["---", "@@", "+++"]
assert all(e in out for e in expected), msg
else:
msg = "Something was modified, but all formatter are deactivated."
assert "Nothing to do!" in out, msg
with FormatterAsserter(
f'"""{"a" * 120}\n{"b" * 120}"""', FORMATTERS, capsys, tmp_path
) as asserter:
asserter.assert_format_when_activated()
asserter.assert_no_change_when_deactivated()