Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Current usage of ``pydocstringformatter``:
.. code-block:: shell

usage: pydocstringformatter [-h] [-w] [--quiet] [-v] [--exclude EXCLUDE]
[--exit-code]
[--max-summary-lines MAX_SUMMARY_LINES]
[--summary-quotes-same-line]
[--split-summary-body --no-split-summary-body]
Expand All @@ -30,6 +31,9 @@ Current usage of ``pydocstringformatter``:
configuration:
--exclude EXCLUDE A comma separated list of glob patterns of file path
names not to be formatted.
--exit-code Turn on if the program should exit with bitwise exit
codes. 0 = No changes, 32 = Changed files or printed
diff.
--max-summary-lines MAX_SUMMARY_LINES
The maximum numbers of lines a summary can span. The
default value is 1.
Expand Down
10 changes: 10 additions & 0 deletions pydocstringformatter/configuration/arguments_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ def _register_arguments(self, version: str) -> None:
),
)

self.configuration_group.add_argument(
"--exit-code",
action="store_true",
default=False,
help=(
"Turn on if the program should exit with bitwise exit codes. "
"0 = No changes, 32 = Changed files or printed diff."
),
)

self.configuration_group.add_argument(
"--max-summary-lines",
action="store",
Expand Down
44 changes: 30 additions & 14 deletions pydocstringformatter/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,37 @@ def __init__(self, argv: Union[List[str], None]) -> None:
)
self.config = self._arguments_manager.namespace

if argv := argv or sys.argv[1:]:
self._arguments_manager.parse_options(argv)
for formatter in formatting.FORMATTERS:
formatter.set_config_namespace(self.config)
self._check_files(self.config.files)
else:
# Display help message if nothing is passed
if not (argv := argv or sys.argv[1:]):
self._arguments_manager.print_help()
return

# Parse options and register on formatters
self._arguments_manager.parse_options(argv)
for formatter in formatting.FORMATTERS:
formatter.set_config_namespace(self.config)

def _check_files(self, arguments: List[str]) -> None:
self._check_files(self.config.files)

def _check_files(self, files: List[str]) -> None:
"""Find all files and perform the formatting."""
filepaths = utils._find_python_files(arguments, self.config.exclude)
self._format_files(filepaths)
filepaths = utils._find_python_files(files, self.config.exclude)

is_changed = self._format_files(filepaths)

if not is_changed: # pylint: disable=consider-using-assignment-expr
if len(filepaths) > 1:
files_string = f"{len(filepaths)} files"
else:
files_string = "1 file"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
files_string = "1 file"
files_string = f"'{filepaths[0].name}'"

Maybe a nice little bonus ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fails with IndexError in some test cases. I'll keep it in mind for a future enhancement.


utils._print_to_console(
f"Nothing to do! All docstrings in {files_string} are correct 🎉\n",
self.config.quiet,
)
utils._sys_exit(0, self.config.exit_code)
else:
utils._sys_exit(32, self.config.exit_code)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not is_changed: # pylint: disable=consider-using-assignment-expr
if len(filepaths) > 1:
files_string = f"{len(filepaths)} files"
else:
files_string = "1 file"
utils._print_to_console(
f"Nothing to do! All docstrings in {files_string} are correct 🎉\n",
self.config.quiet,
)
utils._sys_exit(0, self.config.exit_code)
else:
utils._sys_exit(32, self.config.exit_code)
if is_changed: # pylint: disable=consider-using-assignment-expr
utils._sys_exit(32, self.config.exit_code)
return # We may not sys exit depending on options
files_string = f"{len(filepaths)}"
files_string += "files" if len(filepaths) > 1 else "file"
utils._print_to_console(
f"Nothing to do! All docstrings in {files_string} are correct 🎉\n",
self.config.quiet,
)
utils._sys_exit(0, self.config.exit_code)

You know my favorite thing in the world is premature return I just had to say it 😄 Depending on what you think of displaying the name of a file if it's unique there the suggestion might need some tweaking.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it around a little bit. You actually found a small bug: for 0 files checked due to exclude patterns we still said that 1 file was correct.

I changed it (and fixed the tests).


def _format_file(self, filename: Path) -> bool:
"""Format a file."""
Expand Down Expand Up @@ -83,14 +102,11 @@ def _format_file(self, filename: Path) -> bool:

return is_changed

def _format_files(self, filepaths: List[Path]) -> None:
def _format_files(self, filepaths: List[Path]) -> bool:
"""Format a list of files."""
is_changed = False

for file in filepaths:
is_changed = self._format_file(file) or is_changed

if not is_changed:
utils._print_to_console(
"Nothing to do! All docstrings are correct 🎉\n", self.config.quiet
)
return is_changed
3 changes: 2 additions & 1 deletion pydocstringformatter/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pydocstringformatter.utils.file_diference import _generate_diff
from pydocstringformatter.utils.find_docstrings import _is_docstring
from pydocstringformatter.utils.find_python_file import _find_python_files
from pydocstringformatter.utils.output import _print_to_console
from pydocstringformatter.utils.output import _print_to_console, _sys_exit

__all__ = [
"_find_python_files",
Expand All @@ -16,4 +16,5 @@
"PydocstringFormatterError",
"TomlParsingError",
"_print_to_console",
"_sys_exit",
]
6 changes: 6 additions & 0 deletions pydocstringformatter/utils/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ def _print_to_console(string: str, quiet: bool) -> None:
"""
if not quiet:
sys.stdout.buffer.write(_encode_string(string))


def _sys_exit(value: int, option: bool) -> None:
"""Sys.exit if the boolean passed says to do so."""
if option:
sys.exit(value)
10 changes: 5 additions & 5 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_valid_toml(
monkeypatch.chdir(CONFIG_DATA / "valid_toml")
pydocstringformatter.run_docstring_formatter(["test_package"])
output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err


Expand Down Expand Up @@ -175,7 +175,7 @@ def test_exclude_match(
monkeypatch.chdir(CONFIG_DATA / "exclude_match")
pydocstringformatter.run_docstring_formatter(["test_package"])
output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err

@staticmethod
Expand All @@ -186,7 +186,7 @@ def test_exclude_match_inner_directory(
monkeypatch.chdir(CONFIG_DATA / "exclude_match_inner")
pydocstringformatter.run_docstring_formatter(["test_package"])
output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err

@staticmethod
Expand All @@ -197,7 +197,7 @@ def test_exclude_csv_string(
monkeypatch.chdir(CONFIG_DATA / "exclude_match_csv")
pydocstringformatter.run_docstring_formatter(["test_package"])
output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err

@staticmethod
Expand All @@ -208,7 +208,7 @@ def test_exclude_csv_list(
monkeypatch.chdir(CONFIG_DATA / "exclude_match_csv_list")
pydocstringformatter.run_docstring_formatter(["test_package"])
output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err

@staticmethod
Expand Down
56 changes: 50 additions & 6 deletions tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,19 @@ class OptionalFormatter(StringFormatter):
name = "optional-formatter"
optional = True

def _treat_string(self, tokeninfo: tokenize.TokenInfo, _: int) -> str:
@staticmethod
def _treat_string(tokeninfo: tokenize.TokenInfo, _: int) -> str:
"""Treat a string."""
return tokeninfo.string

class NonOptionalFormatter(StringFormatter):
"""A non-optional formatter."""

name = "non-optional-formatter"

def _treat_string(self, tokeninfo: tokenize.TokenInfo, _: int) -> str:
@staticmethod
def _treat_string(tokeninfo: tokenize.TokenInfo, _: int) -> str:
"""Treat a string."""
return tokeninfo.string

FORMATTERS.append(OptionalFormatter())
Expand Down Expand Up @@ -96,15 +100,15 @@ def test_output_message_nothing_done(
"""Test that we emit the correct message when nothing was done."""
with open(test_file, "w", encoding="utf-8") as file:
file.write('"""A multi-line\ndocstring.\n"""')
with open(test_file + "2", "w", encoding="utf-8") as file:
with open(test_file.replace(".py", "2.py"), "w", encoding="utf-8") as file:
file.write('"""A multi-line\ndocstring.\n"""')

pydocstringformatter.run_docstring_formatter(
[str(Path(test_file).parent), "--write"]
)

output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 2 files are correct 🎉\n"
assert not output.err


Expand All @@ -117,8 +121,8 @@ def test_output_message_one_file(
except ValueError:
expected_path = test_file

with open(test_file + "2", "w", encoding="utf-8") as file:
file.write('"""A multi-line\ndocstring\n"""')
with open(test_file.replace(".py", "2.py"), "w", encoding="utf-8") as file:
file.write('"""A multi-line\ndocstring.\n"""')

pydocstringformatter.run_docstring_formatter(
[str(Path(test_file).parent), "--write"]
Expand Down Expand Up @@ -179,3 +183,43 @@ def test_optional_formatters_argument(
) as asserter:
asserter.assert_format_when_activated()
asserter.assert_no_change_when_deactivated()


class TestExitCodes:
"""Tests for the --exit-code option."""

@staticmethod
def test_exit_code_with_write(test_file: str) -> None:
"""Test that we emit the correct exit code in write mode."""
with pytest.raises(SystemExit) as exit_exec:
pydocstringformatter.run_docstring_formatter(
[str(Path(test_file)), "--write", "--exit-code"]
)

assert exit_exec.value.code == 32

# After first writing changes, now we expect no changes
with pytest.raises(SystemExit) as exit_exec:
pydocstringformatter.run_docstring_formatter(
[str(Path(test_file)), "--write", "--exit-code"]
)

assert not exit_exec.value.code

@staticmethod
def test_exit_code_without_write(test_file: str) -> None:
"""Test that we emit the correct exit code in write mode."""
with pytest.raises(SystemExit) as exit_exec:
pydocstringformatter.run_docstring_formatter(
[str(Path(test_file)), "--exit-code"]
)

assert exit_exec.value.code == 32

# We expect an exit code on both occassions
with pytest.raises(SystemExit) as exit_exec:
pydocstringformatter.run_docstring_formatter(
[str(Path(test_file)), "--exit-code"]
)

assert exit_exec.value.code == 32
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,5 @@ def test_encoding_of_console_messages(
pydocstringformatter.run_docstring_formatter([test_file, "--write"])

output = capsys.readouterr()
assert output.out == "Nothing to do! All docstrings are correct 🎉\n"
assert output.out == "Nothing to do! All docstrings in 1 file are correct 🎉\n"
assert not output.err