From 82213749dbe05231317eb9b68af0ce5a0b3ca588 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Wed, 22 May 2024 10:32:31 +0100 Subject: [PATCH 1/4] Correct type hints for search_issues search_issues can return either a dictionary (if json_result) or a list of Issues under default behavior. This change provides separate hints using overload so that type checked code doesn't need to cast unnecessarily when dealing with output. --- jira/client.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/jira/client.py b/jira/client.py index ff311bcd6..00bdb5dff 100644 --- a/jira/client.py +++ b/jira/client.py @@ -3485,6 +3485,34 @@ def resolution(self, id: str) -> Resolution: # Search + @overload + def search_issues( + self, + jql_str: str, + startAt: int = 0, + maxResults: int = 50, + validate_query: bool = True, + fields: str | list[str] | None = "*all", + expand: str | None = None, + properties: str | None = None, + json_result: Literal[False] = False, + use_post: bool = False, + ) -> ResultList[Issue]: ... + + @overload + def search_issues( + self, + jql_str: str, + startAt: int = 0, + maxResults: int = 50, + validate_query: bool = True, + fields: str | list[str] | None = "*all", + expand: str | None = None, + properties: str | None = None, + json_result: Literal[True] = True, + use_post: bool = False, + ) -> dict[str, Any]: ... + def search_issues( self, jql_str: str, From b47f3d044942e5415b13bd61a250cbf1ccbf6230 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Wed, 22 May 2024 11:06:23 +0100 Subject: [PATCH 2/4] Avoid boolean trap in search_issues We now require json_result and use_post are passed as keyword arguments, satisfying type checker requirements and handling the default return value correctly. --- jira/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jira/client.py b/jira/client.py index 00bdb5dff..cbbcbd8d9 100644 --- a/jira/client.py +++ b/jira/client.py @@ -3495,6 +3495,7 @@ def search_issues( fields: str | list[str] | None = "*all", expand: str | None = None, properties: str | None = None, + *, json_result: Literal[False] = False, use_post: bool = False, ) -> ResultList[Issue]: ... @@ -3509,7 +3510,8 @@ def search_issues( fields: str | list[str] | None = "*all", expand: str | None = None, properties: str | None = None, - json_result: Literal[True] = True, + *, + json_result: Literal[True], use_post: bool = False, ) -> dict[str, Any]: ... @@ -3522,6 +3524,7 @@ def search_issues( fields: str | list[str] | None = "*all", expand: str | None = None, properties: str | None = None, + *, json_result: bool = False, use_post: bool = False, ) -> dict[str, Any] | ResultList[Issue]: From 1690630f466897c60a64c8eeecd155fed6f7c3c1 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Wed, 22 May 2024 11:10:47 +0100 Subject: [PATCH 3/4] No longer cast issues result As the correct type hint is now provided, this is no longer necessary. --- examples/auth.py | 5 +---- tests/tests.py | 6 ------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/examples/auth.py b/examples/auth.py index cda716403..2cb53a8f5 100644 --- a/examples/auth.py +++ b/examples/auth.py @@ -3,7 +3,6 @@ from __future__ import annotations from collections import Counter -from typing import cast from jira import JIRA from jira.client import ResultList @@ -25,9 +24,7 @@ props = jira.application_properties() # Find all issues reported by the admin -# Note: we cast() for mypy's benefit, as search_issues can also return the raw json ! -# This is if the following argument is used: `json_result=True` -issues = cast(ResultList[Issue], jira.search_issues("assignee=admin")) +issues: ResultList[Issue] = jira.search_issues("assignee=admin") # Find the top three projects containing issues reported by admin top_three = Counter([issue.fields.project.key for issue in issues]).most_common(3) diff --git a/tests/tests.py b/tests/tests.py index f040150fa..626efc96a 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -23,7 +23,6 @@ from parameterized import parameterized from jira import JIRA, Issue, JIRAError -from jira.client import ResultList from jira.resources import Dashboard, Resource, cls_for_resource from tests.conftest import JiraTestCase, allow_on_cloud, rndpassword @@ -231,7 +230,6 @@ def setUp(self): def test_search_issues(self): issues = self.jira.search_issues(f"project={self.project_b}") - issues = cast(ResultList[Issue], issues) self.assertLessEqual(len(issues), 50) # default maxResults for issue in issues: self.assertTrue(issue.key.startswith(self.project_b)) @@ -243,7 +241,6 @@ def test_search_issues_async(self): issues = self.jira.search_issues( f"project={self.project_b}", maxResults=False ) - issues = cast(ResultList[Issue], issues) self.assertEqual(len(issues), issues.total) for issue in issues: self.assertTrue(issue.key.startswith(self.project_b)) @@ -263,7 +260,6 @@ def test_search_issues_startat(self): def test_search_issues_field_limiting(self): issues = self.jira.search_issues(f"key={self.issue}", fields="summary,comment") - issues = cast(ResultList[Issue], issues) self.assertTrue(hasattr(issues[0].fields, "summary")) self.assertTrue(hasattr(issues[0].fields, "comment")) self.assertFalse(hasattr(issues[0].fields, "reporter")) @@ -271,7 +267,6 @@ def test_search_issues_field_limiting(self): def test_search_issues_expand(self): issues = self.jira.search_issues(f"key={self.issue}", expand="changelog") - issues = cast(ResultList[Issue], issues) # self.assertTrue(hasattr(issues[0], 'names')) self.assertEqual(len(issues), 1) self.assertFalse(hasattr(issues[0], "editmeta")) @@ -283,7 +278,6 @@ def test_search_issues_use_post(self): with pytest.raises(JIRAError): self.jira.search_issues(long_jql) issues = self.jira.search_issues(long_jql, use_post=True) - issues = cast(ResultList[Issue], issues) self.assertEqual(len(issues), 1) self.assertEqual(issues[0].key, self.issue) From 41be7a02b9ac8ce5f2657529ef34ffbf573857aa Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Wed, 22 May 2024 16:15:10 +0100 Subject: [PATCH 4/4] Add test for json_results variant of search_issues --- tests/tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/tests.py b/tests/tests.py index 626efc96a..4ff6ab2e2 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -234,6 +234,13 @@ def test_search_issues(self): for issue in issues: self.assertTrue(issue.key.startswith(self.project_b)) + def test_search_issues_json(self): + result = self.jira.search_issues(f"project={self.project_b}", json_result=True) + issues = result["issues"] + self.assertLessEqual(len(issues), 50) # default maxResults + for issue in issues: + self.assertTrue(issue["key"].startswith(self.project_b)) + def test_search_issues_async(self): original_val = self.jira._options["async"] try: