From ec46df5400296b6335f7ce308abbfe59240c4583 Mon Sep 17 00:00:00 2001 From: "daniel.eades" Date: Sat, 12 Jul 2025 07:07:51 +0100 Subject: [PATCH] use 'ruff' for linting --- Makefile | 1 + doorstop/__init__.py | 17 +++++++++ doorstop/cli/tests/__init__.py | 1 - doorstop/common.py | 18 ++++----- doorstop/core/__init__.py | 9 +++++ .../publishers/tests/test_publisher_html.py | 15 -------- .../tests/test_publisher_html_doc.py | 1 - .../tests/test_publisher_latex_doc.py | 1 - .../tests/test_publisher_markdown.py | 1 - .../tests/test_publisher_markdown_doc.py | 1 - .../publishers/tests/test_publisher_text.py | 10 ----- doorstop/core/publishers/text.py | 6 ++- doorstop/core/tests/test_all.py | 4 +- .../core/tests/validators/validator_dummy.py | 2 +- doorstop/server/__init__.py | 5 +++ poetry.lock | 38 ++++++++++++++++--- pyproject.toml | 1 + reqs/ext/.req_sha_item_validator.py | 5 +-- 18 files changed, 84 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 766799829..85e1ed07e 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ endif poetry run mypy $(PACKAGES) --config-file=.mypy.ini poetry run pylint $(PACKAGES) --rcfile=.pylint.ini poetry run pydocstyle $(PACKAGES) $(CONFIG) + poetry run ruff check # TESTS ####################################################################### diff --git a/doorstop/__init__.py b/doorstop/__init__.py index 29127a28d..6458cc94e 100644 --- a/doorstop/__init__.py +++ b/doorstop/__init__.py @@ -21,6 +21,23 @@ __project__ = "Doorstop" +__all__ = [ + "DoorstopError", + "DoorstopInfo", + "DoorstopWarning", + "Document", + "Item", + "Tree", + "build", + "builder", + "editor", + "exporter", + "find_document", + "find_item", + "importer", + "publisher", +] + try: __version__ = version(__project__) except PackageNotFoundError: diff --git a/doorstop/cli/tests/__init__.py b/doorstop/cli/tests/__init__.py index 8c74b5e5f..a85315dcc 100644 --- a/doorstop/cli/tests/__init__.py +++ b/doorstop/cli/tests/__init__.py @@ -6,7 +6,6 @@ import unittest from doorstop import settings -from doorstop.cli.main import main ROOT = os.path.join(os.path.dirname(__file__), "..", "..", "..") REQS = os.path.join(ROOT, "reqs") diff --git a/doorstop/common.py b/doorstop/common.py index 4f81417a7..1e3e6ebad 100644 --- a/doorstop/common.py +++ b/doorstop/common.py @@ -307,30 +307,30 @@ def update_data_from_markdown_content(data, content, textattributekeys): if "header" in textattributekeys: # search for first content line and check # if it is a h1 header - for l in s: + for line in s: # skip empty lines - if len(l.strip()) == 0: + if len(line.strip()) == 0: continue # check if first found line is a header - m = h1.match(l.strip()) + m = h1.match(line.strip()) if m: # header found header = m.group(1) else: # no header found, add to normal text - text += l + text += line break # if header was found, skip empty lines before main text if header: - for l in s: - if len(l.strip()) != 0: - text += l + for line in s: + if len(line.strip()) != 0: + text += line break # remaining content is normal text - for l in s: - text += l + for line in s: + text += line if "header" in textattributekeys and header: data["header"] = header diff --git a/doorstop/core/__init__.py b/doorstop/core/__init__.py index a61249d9d..a0d57c27c 100644 --- a/doorstop/core/__init__.py +++ b/doorstop/core/__init__.py @@ -6,3 +6,12 @@ from doorstop.core.document import Document from doorstop.core.item import Item from doorstop.core.tree import Tree + +__all__ = [ + "build", + "find_document", + "find_item", + "Document", + "Item", + "Tree", +] diff --git a/doorstop/core/publishers/tests/test_publisher_html.py b/doorstop/core/publishers/tests/test_publisher_html.py index 9f83c5923..47b99f01d 100644 --- a/doorstop/core/publishers/tests/test_publisher_html.py +++ b/doorstop/core/publishers/tests/test_publisher_html.py @@ -5,7 +5,6 @@ # pylint: disable=unused-argument,protected-access import os -import stat import unittest from secrets import token_hex from shutil import rmtree @@ -41,20 +40,6 @@ def tearDownClass(cls): """Remove test folder.""" rmtree("mock_%s" % __name__, onerror=on_error_with_retry) - @patch("os.path.isdir", Mock(side_effect=[False, False, False, False])) - @patch("os.makedirs") - @patch("builtins.open") - def test_publish_document(self, mock_open, mock_makedirs): - """Verify a document can be published.""" - path = os.path.join(self.dirpath, "published.html") - self.document.items = [] - # Act - path2 = publisher.publish(self.document, path) - # Assert - self.assertIs(path, path2) - mock_makedirs.assert_called_once_with(self.dirpath) - mock_open.assert_called_once_with(path, "wb") - @patch("os.path.isdir", Mock(return_value=False)) @patch("os.makedirs") @patch("builtins.open") diff --git a/doorstop/core/publishers/tests/test_publisher_html_doc.py b/doorstop/core/publishers/tests/test_publisher_html_doc.py index 5c4d91635..8c9861a75 100644 --- a/doorstop/core/publishers/tests/test_publisher_html_doc.py +++ b/doorstop/core/publishers/tests/test_publisher_html_doc.py @@ -5,7 +5,6 @@ # pylint: disable=unused-argument,protected-access import os -import stat import unittest from secrets import token_hex from shutil import rmtree diff --git a/doorstop/core/publishers/tests/test_publisher_latex_doc.py b/doorstop/core/publishers/tests/test_publisher_latex_doc.py index 77ae4011f..f9b3f311e 100644 --- a/doorstop/core/publishers/tests/test_publisher_latex_doc.py +++ b/doorstop/core/publishers/tests/test_publisher_latex_doc.py @@ -5,7 +5,6 @@ # pylint: disable=unused-argument,protected-access import os -import stat import unittest from secrets import token_hex from shutil import rmtree diff --git a/doorstop/core/publishers/tests/test_publisher_markdown.py b/doorstop/core/publishers/tests/test_publisher_markdown.py index 2a4e396a0..eab95fb49 100644 --- a/doorstop/core/publishers/tests/test_publisher_markdown.py +++ b/doorstop/core/publishers/tests/test_publisher_markdown.py @@ -22,7 +22,6 @@ MockItemAndVCS, ) from doorstop.core.tests.helpers import on_error_with_retry -from doorstop.core.types import UID class TestModule(MockDataMixIn, unittest.TestCase): diff --git a/doorstop/core/publishers/tests/test_publisher_markdown_doc.py b/doorstop/core/publishers/tests/test_publisher_markdown_doc.py index ab5b2576d..7eb4fb518 100644 --- a/doorstop/core/publishers/tests/test_publisher_markdown_doc.py +++ b/doorstop/core/publishers/tests/test_publisher_markdown_doc.py @@ -5,7 +5,6 @@ # pylint: disable=unused-argument,protected-access import os -import stat import unittest from secrets import token_hex from shutil import rmtree diff --git a/doorstop/core/publishers/tests/test_publisher_text.py b/doorstop/core/publishers/tests/test_publisher_text.py index 0223ae8a8..b0cf4a3a6 100644 --- a/doorstop/core/publishers/tests/test_publisher_text.py +++ b/doorstop/core/publishers/tests/test_publisher_text.py @@ -5,7 +5,6 @@ # pylint: disable=unused-argument,protected-access import os -import stat import unittest from secrets import token_hex from shutil import rmtree @@ -108,15 +107,6 @@ def test_lines_text_item_with_child_links(self): # Assert self.assertIn("Child links: tst1", text) - def test_lines_text_item(self): - """Verify text can be published from an item.""" - with patch.object( - self.item5, "find_ref", Mock(return_value=("path/to/mock/file", 42)) - ): - lines = publisher.publish_lines(self.item5, ".txt") - text = "".join(line + "\n" for line in lines) - self.assertIn("Reference: path/to/mock/file (line 42)", text) - @patch("doorstop.settings.ENABLE_HEADERS", True) def test_setting_enable_headers_true(self): """Verify that the settings.ENABLE_HEADERS changes the output appropriately when True.""" diff --git a/doorstop/core/publishers/text.py b/doorstop/core/publishers/text.py index b1dfa5a5e..fd8ad6130 100644 --- a/doorstop/core/publishers/text.py +++ b/doorstop/core/publishers/text.py @@ -102,13 +102,15 @@ def lines(self, obj, **_): label = "Parent links: " else: label = "Links: " - slinks = label + ", ".join(str(l) for l in item.links) + slinks = label + ", ".join(str(link) for link in item.links) yield from self._chunks(slinks) if settings.PUBLISH_CHILD_LINKS: links = item.find_child_links() if links: yield "" # break before links - slinks = "Child links: " + ", ".join(str(l) for l in links) + slinks = "Child links: " + ", ".join( + str(link) for link in links + ) yield from self._chunks(slinks) # Attributes diff --git a/doorstop/core/tests/test_all.py b/doorstop/core/tests/test_all.py index 243dc6cc0..a0d9189bb 100644 --- a/doorstop/core/tests/test_all.py +++ b/doorstop/core/tests/test_all.py @@ -182,7 +182,7 @@ def test_issues_duplicate_level(self): expect = DoorstopWarning("duplicate level: 2.1 (REQ002, REQ2-001)") for issue in self.document.issues: logging.info(repr(issue)) - if type(issue) == type(expect) and issue.args == expect.args: + if type(issue) is type(expect) and issue.args == expect.args: break else: self.fail("issue not found: {}".format(expect)) @@ -194,7 +194,7 @@ def test_issues_skipped_level(self): expect = DoorstopInfo("skipped level: 1.2.3 (REQ001), 1.4 (REQ003)") for issue in self.document.issues: logging.info(repr(issue)) - if type(issue) == type(expect) and issue.args == expect.args: + if type(issue) is type(expect) and issue.args == expect.args: break else: self.fail("issue not found: {}".format(expect)) diff --git a/doorstop/core/tests/validators/validator_dummy.py b/doorstop/core/tests/validators/validator_dummy.py index 5aeb389ce..b043ba400 100644 --- a/doorstop/core/tests/validators/validator_dummy.py +++ b/doorstop/core/tests/validators/validator_dummy.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: LGPL-3.0-only -from doorstop import DoorstopError, DoorstopInfo, DoorstopWarning +from doorstop import DoorstopInfo def item_validator(item): diff --git a/doorstop/server/__init__.py b/doorstop/server/__init__.py index 46c1c3f06..cc9fabec1 100644 --- a/doorstop/server/__init__.py +++ b/doorstop/server/__init__.py @@ -3,3 +3,8 @@ """Web interface for Doorstop.""" from .client import check, get_next_number + +__all__ = [ + "check", + "get_next_number", +] diff --git a/poetry.lock b/poetry.lock index d93af4bf4..3ae7bd49f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "altgraph" @@ -475,7 +475,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, @@ -541,7 +541,7 @@ description = "Fork of the standard library cgi and cgitb modules, being depreca optional = false python-versions = "<4.0,>=3.10" groups = ["dev"] -markers = "python_version >= \"3.13\"" +markers = "python_version == \"3.13\"" files = [ {file = "legacy_cgi-2.6.1-py3-none-any.whl", hash = "sha256:8eacc1522d9f76451337a4b5a0abf494158d39250754b0d1bc19a14c6512af9b"}, {file = "legacy_cgi-2.6.1.tar.gz", hash = "sha256:f2ada99c747c3d72a473a6aaff6259a61f226b06fe9f3106e495ab83fd8f7a42"}, @@ -1305,6 +1305,34 @@ files = [ {file = "rope-0.14.0.tar.gz", hash = "sha256:c5c5a6a87f7b1a2095fb311135e2a3d1f194f5ecb96900fdd0a9100881f48aaf"}, ] +[[package]] +name = "ruff" +version = "0.12.3" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.12.3-py3-none-linux_armv6l.whl", hash = "sha256:47552138f7206454eaf0c4fe827e546e9ddac62c2a3d2585ca54d29a890137a2"}, + {file = "ruff-0.12.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0a9153b000c6fe169bb307f5bd1b691221c4286c133407b8827c406a55282041"}, + {file = "ruff-0.12.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fa6b24600cf3b750e48ddb6057e901dd5b9aa426e316addb2a1af185a7509882"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2506961bf6ead54887ba3562604d69cb430f59b42133d36976421bc8bd45901"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4faaff1f90cea9d3033cbbcdf1acf5d7fb11d8180758feb31337391691f3df0"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40dced4a79d7c264389de1c59467d5d5cefd79e7e06d1dfa2c75497b5269a5a6"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0262d50ba2767ed0fe212aa7e62112a1dcbfd46b858c5bf7bbd11f326998bafc"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12371aec33e1a3758597c5c631bae9a5286f3c963bdfb4d17acdd2d395406687"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:560f13b6baa49785665276c963edc363f8ad4b4fc910a883e2625bdb14a83a9e"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023040a3499f6f974ae9091bcdd0385dd9e9eb4942f231c23c57708147b06311"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:883d844967bffff5ab28bba1a4d246c1a1b2933f48cb9840f3fdc5111c603b07"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2120d3aa855ff385e0e562fdee14d564c9675edbe41625c87eeab744a7830d12"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b16647cbb470eaf4750d27dddc6ebf7758b918887b56d39e9c22cce2049082b"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e1417051edb436230023575b149e8ff843a324557fe0a265863b7602df86722f"}, + {file = "ruff-0.12.3-py3-none-win32.whl", hash = "sha256:dfd45e6e926deb6409d0616078a666ebce93e55e07f0fb0228d4b2608b2c248d"}, + {file = "ruff-0.12.3-py3-none-win_amd64.whl", hash = "sha256:a946cf1e7ba3209bdef039eb97647f1c77f6f540e5845ec9c114d3af8df873e7"}, + {file = "ruff-0.12.3-py3-none-win_arm64.whl", hash = "sha256:5f9c7c9c8f84c2d7f27e93674d27136fbf489720251544c4da7fb3d742e011b1"}, + {file = "ruff-0.12.3.tar.gz", hash = "sha256:f1b5a4b6668fd7b7ea3697d8d98857390b40c1320a63a178eee6be0899ea2d77"}, +] + [[package]] name = "setuptools" version = "69.1.1" @@ -1631,7 +1659,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, @@ -1644,4 +1672,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.1" python-versions = "<3.14,>=3.9" -content-hash = "a8350d05a7474404b18368987c300ca1b29c64a8bf9d1af160588ae5f004bf34" +content-hash = "5857d6ecf7e9178f722241a34f12417370bc6108543240d3758cc56ae3515884" diff --git a/pyproject.toml b/pyproject.toml index a485a1f89..eeadeb023 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,7 @@ isort = "^5.12" mypy = ">=1.1.1" pydocstyle = "*" pylint = "~3.2.0" +ruff = "^0.12.3" types-markdown = "*" types-pyyaml = "*" types-requests = "*" diff --git a/reqs/ext/.req_sha_item_validator.py b/reqs/ext/.req_sha_item_validator.py index 3012029e3..5a5d7485f 100644 --- a/reqs/ext/.req_sha_item_validator.py +++ b/reqs/ext/.req_sha_item_validator.py @@ -1,12 +1,11 @@ # SPDX-License-Identifier: LGPL-3.0-only -from doorstop import DoorstopInfo, DoorstopWarning, DoorstopError +from doorstop import DoorstopWarning from subprocess import check_output from copy import copy -from random import random def item_validator(item): - if getattr(item, "references") == None: + if getattr(item, "references") is None: return [] for ref in item.references: