[PATCH 07/14] Add ty checks to CI
Tamir Duberstein <[email protected]>
| Newsgroups | org.kernel.linux.tools |
|---|---|
| Message-ID | <[email protected]> |
Add ty to the development dependencies and enable all ty rules. Run ty in the b4 CI check script so its diagnostics are surfaced with the other checks. Use `ty check --add-ignore` to suppress existing errors. Signed-off-by: Tamir Duberstein <[email protected]> --- pyproject.toml | 4 ++++ src/liblore/node.py | 2 +- tests/test_auth_headers.py | 10 +++++----- tests/test_node.py | 30 +++++++++++++++--------------- tools/b4-ci-check.py | 6 ++++++ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a160edd..37c85a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dev = [ "pytest-asyncio", "responses", "ruff", + "ty", "types-requests", ] @@ -63,6 +64,9 @@ executionEnvironments = [ { root = "tests", reportPrivateUsage = false }, ] +[tool.ty.rules] +all = "error" + [tool.ruff.lint] extend-select = ["ARG", "I"] diff --git a/src/liblore/node.py b/src/liblore/node.py index 095ba4f..1b0463e 100644 --- a/src/liblore/node.py +++ b/src/liblore/node.py @@ -319,7 +319,7 @@ class LoreNode: except ValueError: pass - node = cls(url, **kwargs) # type: ignore[arg-type] + node = cls(url, **kwargs) # type: ignore[arg-type] # ty:ignore[invalid-argument-type] val = gitcfg.get('useragentplus') if isinstance(val, str) and val: diff --git a/tests/test_auth_headers.py b/tests/test_auth_headers.py index f1c240a..b51050c 100644 --- a/tests/test_auth_headers.py +++ b/tests/test_auth_headers.py @@ -29,7 +29,7 @@ class TestAuthHeadersImport: def test_ok_when_authheaders_installed(self) -> None: fake = ModuleType('authheaders') - fake.authenticate_message = MagicMock() # type: ignore[attr-defined] + fake.authenticate_message = MagicMock() # type: ignore[attr-defined] # ty:ignore[unresolved-attribute] with patch.dict(sys.modules, {'authheaders': fake}): node = LoreNode(add_auth_headers=True) assert node._authheaders is not None @@ -56,7 +56,7 @@ class TestAuthenticateMsgs: def test_adds_header_when_enabled(self) -> None: fake = ModuleType('authheaders') - fake.authenticate_message = MagicMock( # type: ignore[attr-defined] + fake.authenticate_message = MagicMock( # type: ignore[attr-defined] # ty:ignore[unresolved-attribute] return_value='Authentication-Results: liblore; dkim=pass header.d=example.com', ) with patch.dict(sys.modules, {'authheaders': fake}): @@ -82,7 +82,7 @@ class TestAuthenticateMsgs: def test_skips_empty_result(self) -> None: fake = ModuleType('authheaders') - fake.authenticate_message = MagicMock(return_value='') # type: ignore[attr-defined] + fake.authenticate_message = MagicMock(return_value='') # type: ignore[attr-defined] # ty:ignore[unresolved-attribute] with patch.dict(sys.modules, {'authheaders': fake}): node = LoreNode(add_auth_headers=True) msg = EmailMessage() @@ -95,7 +95,7 @@ class TestAuthenticateMsgs: def test_multiple_messages(self) -> None: fake = ModuleType('authheaders') - fake.authenticate_message = MagicMock( # type: ignore[attr-defined] + fake.authenticate_message = MagicMock( # type: ignore[attr-defined] # ty:ignore[unresolved-attribute] side_effect=[ 'liblore; dkim=pass', 'Authentication-Results: liblore; dkim=fail', @@ -126,7 +126,7 @@ class TestAuthInFetchMethods: @pytest.fixture() def auth_node(self) -> Iterator[tuple[LoreNode, responses.RequestsMock]]: fake = ModuleType('authheaders') - fake.authenticate_message = MagicMock( # type: ignore[attr-defined] + fake.authenticate_message = MagicMock( # type: ignore[attr-defined] # ty:ignore[unresolved-attribute] return_value='Authentication-Results: liblore; dkim=pass', ) with patch.dict(sys.modules, {'authheaders': fake}): diff --git a/tests/test_node.py b/tests/test_node.py index fc94d9f..f4a3495 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -500,19 +500,19 @@ class TestBatchGetThreadByMsgid: node = LoreNode() thread_a = [EmailMessage()] thread_b = [EmailMessage(), EmailMessage()] - node.get_thread_by_msgid = MagicMock(side_effect=[thread_a, thread_b]) # type: ignore[method-assign] + node.get_thread_by_msgid = MagicMock(side_effect=[thread_a, thread_b]) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_msgid(['a@x', 'b@x']) assert results == [thread_a, thread_b] - assert node.get_thread_by_msgid.call_count == 2 + assert node.get_thread_by_msgid.call_count == 2 # ty:ignore[unresolved-attribute] mock_sleep.assert_called_once_with(0.1) def test_no_sleep_for_single_msgid(self) -> None: node = LoreNode() thread = [EmailMessage()] - node.get_thread_by_msgid = MagicMock(return_value=thread) # type: ignore[method-assign] + node.get_thread_by_msgid = MagicMock(return_value=thread) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_msgid(['only@x']) @@ -522,7 +522,7 @@ class TestBatchGetThreadByMsgid: def test_passes_kwargs(self) -> None: node = LoreNode() - node.get_thread_by_msgid = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] + node.get_thread_by_msgid = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep'): node.batch_get_thread_by_msgid( @@ -532,7 +532,7 @@ class TestBatchGetThreadByMsgid: since='20240101', ) - node.get_thread_by_msgid.assert_called_once_with( + node.get_thread_by_msgid.assert_called_once_with( # ty:ignore[unresolved-attribute] 'a@x', strict=False, sort=True, @@ -541,7 +541,7 @@ class TestBatchGetThreadByMsgid: def test_sleep_count_matches_gaps(self) -> None: node = LoreNode() - node.get_thread_by_msgid = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] + node.get_thread_by_msgid = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: node.batch_get_thread_by_msgid(['a@x', 'b@x', 'c@x']) @@ -550,14 +550,14 @@ class TestBatchGetThreadByMsgid: def test_empty_list(self) -> None: node = LoreNode() - node.get_thread_by_msgid = MagicMock() # type: ignore[method-assign] + node.get_thread_by_msgid = MagicMock() # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_msgid([]) assert results == [] mock_sleep.assert_not_called() - node.get_thread_by_msgid.assert_not_called() + node.get_thread_by_msgid.assert_not_called() # ty:ignore[unresolved-attribute] # ===================================================================== @@ -570,19 +570,19 @@ class TestBatchGetThreadByQuery: node = LoreNode() result_a = [EmailMessage()] result_b = [EmailMessage(), EmailMessage()] - node.get_thread_by_query = MagicMock(side_effect=[result_a, result_b]) # type: ignore[method-assign] + node.get_thread_by_query = MagicMock(side_effect=[result_a, result_b]) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_query(['q1', 'q2']) assert results == [result_a, result_b] - assert node.get_thread_by_query.call_count == 2 + assert node.get_thread_by_query.call_count == 2 # ty:ignore[unresolved-attribute] mock_sleep.assert_called_once_with(0.1) def test_no_sleep_for_single_query(self) -> None: node = LoreNode() result = [EmailMessage()] - node.get_thread_by_query = MagicMock(return_value=result) # type: ignore[method-assign] + node.get_thread_by_query = MagicMock(return_value=result) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_query(['only_query']) @@ -592,7 +592,7 @@ class TestBatchGetThreadByQuery: def test_sleep_count_matches_gaps(self) -> None: node = LoreNode() - node.get_thread_by_query = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] + node.get_thread_by_query = MagicMock(return_value=[EmailMessage()]) # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: node.batch_get_thread_by_query(['q1', 'q2', 'q3', 'q4']) @@ -601,14 +601,14 @@ class TestBatchGetThreadByQuery: def test_empty_list(self) -> None: node = LoreNode() - node.get_thread_by_query = MagicMock() # type: ignore[method-assign] + node.get_thread_by_query = MagicMock() # type: ignore[method-assign] # ty:ignore[invalid-assignment] with patch('liblore.node.time.sleep') as mock_sleep: results = node.batch_get_thread_by_query([]) assert results == [] mock_sleep.assert_not_called() - node.get_thread_by_query.assert_not_called() + node.get_thread_by_query.assert_not_called() # ty:ignore[unresolved-attribute] # ===================================================================== @@ -1928,7 +1928,7 @@ class TestUserAgentPlusProperty: """Property has no setter — assignment raises AttributeError.""" node = LoreNode() with pytest.raises(AttributeError): - node.user_agent_plus = 'nope' # type: ignore[misc] + node.user_agent_plus = 'nope' # type: ignore[misc] # ty:ignore[invalid-assignment] # ===================================================================== diff --git a/tools/b4-ci-check.py b/tools/b4-ci-check.py index 5690cd9..67278f1 100644 --- a/tools/b4-ci-check.py +++ b/tools/b4-ci-check.py @@ -65,6 +65,12 @@ def main() -> None: # https://github.com/astral-sh/ruff/issues/659 run=run_subprocess('ruff'), ), + Check( + tool='ty', + args=['check', 'src', 'tests'], + pass_summary='ty passed', + run=run_subprocess('ty'), + ), # Mypy can emit JSON via "--output json", but b4 only renders details # as plain text, so preserving the normal formatter is more readable. Check( -- 2.53.0