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
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
TOX_PARALLEL_NO_SPINNER: 1

steps:
- name: Switch to using Python 3.8 by default
- name: Switch to using Python 3.10 by default
uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: "3.10"
- name: Install tox
run: >-
python3 -m
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
id: generate_matrix
uses: coactions/matrix@v4
with:
min_python: "3.10"
max_python: "3.14"
default_python: "3.10"
other_names: |
lint
packaging
Expand Down
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 # Use the ref you want to point at
rev: v5.0.0 # Use the ref you want to point at
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-executables-have-shebangs
- repo: https://github.com/asottile/pyupgrade
rev: v3.17.0
rev: v3.20.0
hooks:
- id: pyupgrade
- repo: https://github.com/psf/black
rev: 24.8.0
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
rev: 7.2.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
rev: v1.16.0
hooks:
- id: mypy
# empty args needed in order to match mypy cli behavior
Expand All @@ -33,7 +33,7 @@ repos:
- types-setuptools
- types-docutils
- repo: https://github.com/PyCQA/pylint
rev: v3.2.6
rev: v3.3.7
hooks:
- id: pylint
additional_dependencies:
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ classifiers = [
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
]
Expand All @@ -34,7 +34,7 @@ keywords = [
"rst",
"linter",
]
requires-python = ">=3.8"
requires-python = ">=3.10"
dependencies = [
# Ceiled due to DeprecationWarning: The frontend.OptionParser class will be
# replaced by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.
Expand Down Expand Up @@ -78,7 +78,7 @@ module = [
ignore_missing_imports = true

[tool.pylint.MAIN]
py-version = "3.8.0"
py-version = "3.10.0"

[tool.pylint."MESSAGES CONTROL"]

Expand Down
7 changes: 3 additions & 4 deletions src/doc8/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ def __init__(self):
def total_errors(self):
return len(self.errors)

# pylint: disable=too-many-positional-arguments
def error(self, check_name, filename, line_num, code, message):
self.errors.append((check_name, filename, line_num, code, message))

Expand Down Expand Up @@ -434,8 +435,7 @@ def main():
"--config",
metavar="path",
action="append",
help="user config file location"
" (default: %s)." % ", ".join(CONFIG_FILENAMES),
help="user config file location (default: %s)." % ", ".join(CONFIG_FILENAMES),
default=defaults["config"],
)
parser.add_argument(
Expand Down Expand Up @@ -495,8 +495,7 @@ def main():
action="store",
metavar="int",
type=int,
help="maximum allowed line"
" length (default: %s)." % defaults["max_line_length"],
help="maximum allowed line length (default: %s)." % defaults["max_line_length"],
default=defaults["max_line_length"],
)
parser.add_argument(
Expand Down
120 changes: 75 additions & 45 deletions src/doc8/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@ class TestCommandLine(unittest.TestCase):
"""

def test_main__no_quiet_no_verbose__output_is_not_quiet(self):
with TmpFs() as tmpfs, Capture() as (out, err), patch(
"argparse._sys.argv", ["doc8", tmpfs.path]
with (
TmpFs() as tmpfs,
Capture() as (out, err),
patch("argparse._sys.argv", ["doc8", tmpfs.path]),
):
tmpfs.mock()
state = main()
Expand All @@ -159,8 +161,10 @@ def test_main__no_quiet_no_verbose__output_is_not_quiet(self):
self.assertEqual(state, 1)

def test_main__quiet_no_verbose__output_is_quiet(self):
with TmpFs() as tmpfs, Capture() as (out, err), patch(
"argparse._sys.argv", ["doc8", "--quiet", tmpfs.path]
with (
TmpFs() as tmpfs,
Capture() as (out, err),
patch("argparse._sys.argv", ["doc8", "--quiet", tmpfs.path]),
):
tmpfs.mock()
state = main()
Expand All @@ -169,8 +173,10 @@ def test_main__quiet_no_verbose__output_is_quiet(self):
self.assertEqual(state, 1)

def test_main__no_quiet_verbose__output_is_verbose(self):
with TmpFs() as tmpfs, Capture() as (out, err), patch(
"argparse._sys.argv", ["doc8", "--verbose", tmpfs.path]
with (
TmpFs() as tmpfs,
Capture() as (out, err),
patch("argparse._sys.argv", ["doc8", "--verbose", tmpfs.path]),
):
tmpfs.mock()
state = main()
Expand Down Expand Up @@ -282,8 +288,9 @@ def test_args__no_args__defaults(self):

def test_args__paths__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "path1", "path2"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "path1", "path2"]),
):
state = main()
self.assertEqual(state, 0)
Expand All @@ -292,48 +299,58 @@ def test_args__paths__overrides_default(self):
def test_args__config__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
mock_config = MagicMock(return_value={})
with patch("doc8.main.scan", mock_scan), patch(
"doc8.main.extract_config", mock_config
), patch(
"argparse._sys.argv", ["doc8", "--config", "path1", "--config", "path2"]
with (
patch("doc8.main.scan", mock_scan),
patch("doc8.main.extract_config", mock_config),
patch(
"argparse._sys.argv", ["doc8", "--config", "path1", "--config", "path2"]
),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(config=["path1", "path2"]))

def test_args__allow_long_titles__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--allow-long-titles"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--allow-long-titles"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(allow_long_titles=True))

def test_args__ignore__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv",
["doc8", "--ignore", "D002", "--ignore", "D002", "--ignore", "D005"],
with (
patch("doc8.main.scan", mock_scan),
patch(
"argparse._sys.argv",
["doc8", "--ignore", "D002", "--ignore", "D002", "--ignore", "D005"],
),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(ignore={"D002", "D005"}))

def test_args__sphinx__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--no-sphinx"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--no-sphinx"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(sphinx=False))

def test_args__ignore_path__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv",
["doc8", "--ignore-path", "path1", "--ignore-path", "path2"],
with (
patch("doc8.main.scan", mock_scan),
patch(
"argparse._sys.argv",
["doc8", "--ignore-path", "path1", "--ignore-path", "path2"],
),
):
state = main()
self.assertEqual(state, 0)
Expand All @@ -343,15 +360,18 @@ def test_args__ignore_path__overrides_default(self):

def test_args__ignore_path_errors__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv",
[
"doc8",
"--ignore-path-errors",
"path1;D002",
"--ignore-path-errors",
"path2;D005",
],
with (
patch("doc8.main.scan", mock_scan),
patch(
"argparse._sys.argv",
[
"doc8",
"--ignore-path-errors",
"path1;D002",
"--ignore-path-errors",
"path2;D005",
],
),
):
state = main()
self.assertEqual(state, 0)
Expand All @@ -361,26 +381,29 @@ def test_args__ignore_path_errors__overrides_default(self):

def test_args__default_extension__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--default-extension", "rst"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--default-extension", "rst"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(default_extension="rst"))

def test_args__file_encoding__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--file-encoding", "utf8"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--file-encoding", "utf8"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(file_encoding="utf8"))

def test_args__max_line_length__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--max-line-length", "88"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--max-line-length", "88"]),
):
state = main()
self.assertEqual(state, 0)
Expand All @@ -389,8 +412,12 @@ def test_args__max_line_length__overrides_default(self):
def test_args__extension__overrides_default(self):
# ": [".rst", ".txt"],
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--extension", "ext1", "--extension", "ext2"]
with (
patch("doc8.main.scan", mock_scan),
patch(
"argparse._sys.argv",
["doc8", "--extension", "ext1", "--extension", "ext2"],
),
):
state = main()
self.assertEqual(state, 0)
Expand All @@ -400,26 +427,29 @@ def test_args__extension__overrides_default(self):

def test_args__quiet__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--quiet"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--quiet"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(quiet=True))

def test_args__verbose__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--verbose"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--verbose"]),
):
state = main()
self.assertEqual(state, 0)
mock_scan.assert_called_once_with(self.get_args(verbose=True))

def test_args__version__overrides_default(self):
mock_scan = MagicMock(return_value=([], 0))
with patch("doc8.main.scan", mock_scan), patch(
"argparse._sys.argv", ["doc8", "--version"]
with (
patch("doc8.main.scan", mock_scan),
patch("argparse._sys.argv", ["doc8", "--version"]),
):
state = main()
self.assertEqual(state, 0)
Expand Down
5 changes: 3 additions & 2 deletions src/doc8/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
# under the License.

"""doc8 version information."""

try:
from ._version import version as __version__
except ImportError: # pragma: no branch
try:
import pkg_resources
from importlib.metadata import version

__version__ = pkg_resources.get_distribution("doc8").version
__version__ = version("doc8")
except Exception: # pylint: disable=broad-except
# this is the fallback SemVer version picked by setuptools_scm when tag
# information is not available.
Expand Down