python-jira-plus 3.2.0__tar.gz → 4.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_jira_plus-3.2.0/python_jira_plus.egg-info → python_jira_plus-4.1.0}/PKG-INFO +23 -15
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/README.md +22 -9
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/pyproject.toml +3 -2
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus/__init__.py +10 -5
- python_jira_plus-4.1.0/python_jira_plus/consts.py +22 -0
- python_jira_plus-4.1.0/python_jira_plus/helpers.py +16 -0
- python_jira_plus-4.1.0/python_jira_plus/jira_base.py +448 -0
- python_jira_plus-4.1.0/python_jira_plus/jira_cloud.py +87 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus/jira_on_premise.py +27 -36
- python_jira_plus-4.1.0/python_jira_plus/types.py +9 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0/python_jira_plus.egg-info}/PKG-INFO +23 -15
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus.egg-info/SOURCES.txt +6 -1
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus.egg-info/requires.txt +0 -6
- python_jira_plus-4.1.0/tests/test_jira_cloud_write_integration.py +451 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/tests/test_jira_plus.py +0 -18
- python_jira_plus-4.1.0/tests/test_jira_plus_hardening.py +334 -0
- python_jira_plus-4.1.0/tests/test_jira_plus_v41.py +67 -0
- python_jira_plus-3.2.0/python_jira_plus/consts.py +0 -1
- python_jira_plus-3.2.0/python_jira_plus/jira_base.py +0 -320
- python_jira_plus-3.2.0/python_jira_plus/jira_cloud.py +0 -109
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/LICENSE +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/MANIFEST.in +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus/describe_allowed_value.py +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus.egg-info/dependency_links.txt +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/python_jira_plus.egg-info/top_level.txt +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/setup.cfg +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/tests/test_describe_allowed_value.py +0 -0
- {python_jira_plus-3.2.0 → python_jira_plus-4.1.0}/tests/test_jira_cloud_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-jira-plus
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
|
|
5
5
|
Author: Avi Zaguri
|
|
6
6
|
License: MIT
|
|
@@ -21,11 +21,6 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
21
21
|
Requires-Dist: jira>=3.10.5
|
|
22
22
|
Requires-Dist: packaging>=26.2
|
|
23
23
|
Requires-Dist: requests>=2.32.5
|
|
24
|
-
Provides-Extra: dev
|
|
25
|
-
Requires-Dist: pre-commit>=4.5.0; extra == "dev"
|
|
26
|
-
Requires-Dist: pytest>=9.0.1; extra == "dev"
|
|
27
|
-
Requires-Dist: setuptools>=80.9.0; extra == "dev"
|
|
28
|
-
Requires-Dist: wheel>=0.45.1; extra == "dev"
|
|
29
24
|
Dynamic: license-file
|
|
30
25
|
|
|
31
26
|

|
|
@@ -44,7 +39,7 @@ An enhanced Python client for JIRA that extends the functionality of the officia
|
|
|
44
39
|
|
|
45
40
|
## Features
|
|
46
41
|
- ✅ Simplified connection to JIRA Cloud and On-Premise instances
|
|
47
|
-
- ✅
|
|
42
|
+
- ✅ Clear error handling: failures raise (`raise_on_error=True`) or are logged and returned as `None`/`False`. Rate limits (429) and 503 are retried by `jira-python` itself
|
|
48
43
|
- ✅ Built-in pagination for large result sets
|
|
49
44
|
- ✅ Field validation against JIRA metadata
|
|
50
45
|
- ✅ Enhanced issue creation, retrieval, and updating
|
|
@@ -62,7 +57,6 @@ pip install python-jira-plus
|
|
|
62
57
|
## Requirements
|
|
63
58
|
- Python 3.12+
|
|
64
59
|
- `jira` package
|
|
65
|
-
- `retrying` package
|
|
66
60
|
- `custom-python-logger` package
|
|
67
61
|
|
|
68
62
|
---
|
|
@@ -77,13 +71,26 @@ JIRA_TOKEN=your_jira_api_token
|
|
|
77
71
|
JIRA_BASE_URL=your-instance.atlassian.net # Only used if base_url is not provided to constructor
|
|
78
72
|
```
|
|
79
73
|
|
|
74
|
+
## Error handling
|
|
75
|
+
By default, write helpers (`delete_issue`, `create_link`, `transition_issue`, `upload_attachment`,
|
|
76
|
+
`update_issue`) and `get_issue_by_key` log the error and return `False`/`None`. Pass
|
|
77
|
+
`raise_on_error=True` to the constructor to get the original exception instead:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
jira_client = JiraCloud(raise_on_error=True)
|
|
81
|
+
jira_client.get_issue_by_key("PROJ-404") # raises JIRAError instead of returning None
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Validation problems (`ValueError`) and failed searches (`JIRAError`) are always raised.
|
|
85
|
+
Rate limits (429) and 503 are retried by `jira-python`'s own session, not by this package.
|
|
86
|
+
|
|
80
87
|
## Examples
|
|
81
88
|
|
|
82
89
|
### Creating an Issue with Custom Fields
|
|
83
90
|
```python
|
|
84
|
-
from python_jira_plus
|
|
91
|
+
from python_jira_plus import JiraCloud # or JiraOnPremise
|
|
85
92
|
|
|
86
|
-
jira_client =
|
|
93
|
+
jira_client = JiraCloud()
|
|
87
94
|
issue = jira_client.create_issue(
|
|
88
95
|
project_key="PROJ",
|
|
89
96
|
summary="Implement new feature",
|
|
@@ -98,12 +105,13 @@ issue = jira_client.create_issue(
|
|
|
98
105
|
|
|
99
106
|
### Searching for Issues
|
|
100
107
|
```python
|
|
101
|
-
from python_jira_plus
|
|
108
|
+
from python_jira_plus import JiraCloud
|
|
102
109
|
|
|
103
|
-
jira_client =
|
|
110
|
+
jira_client = JiraCloud()
|
|
104
111
|
issues = jira_client.get_objects_by_query(
|
|
105
112
|
query="project = PROJ AND status = 'In Progress' ORDER BY created DESC",
|
|
106
|
-
max_results=50,
|
|
113
|
+
max_results=50, # page size
|
|
114
|
+
limit=200, # optional cap on the total number of issues returned
|
|
107
115
|
specific_fields=["summary", "status", "assignee"],
|
|
108
116
|
json_result=False
|
|
109
117
|
)
|
|
@@ -114,9 +122,9 @@ for issue in issues:
|
|
|
114
122
|
|
|
115
123
|
### Updating an Issue
|
|
116
124
|
```python
|
|
117
|
-
from python_jira_plus
|
|
125
|
+
from python_jira_plus import JiraCloud
|
|
118
126
|
|
|
119
|
-
jira_client =
|
|
127
|
+
jira_client = JiraCloud()
|
|
120
128
|
issue = jira_client.get_issue_by_key(key="PROJ-123", json_result=False)
|
|
121
129
|
|
|
122
130
|
fields_to_update = {
|
|
@@ -14,7 +14,7 @@ An enhanced Python client for JIRA that extends the functionality of the officia
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
- ✅ Simplified connection to JIRA Cloud and On-Premise instances
|
|
17
|
-
- ✅
|
|
17
|
+
- ✅ Clear error handling: failures raise (`raise_on_error=True`) or are logged and returned as `None`/`False`. Rate limits (429) and 503 are retried by `jira-python` itself
|
|
18
18
|
- ✅ Built-in pagination for large result sets
|
|
19
19
|
- ✅ Field validation against JIRA metadata
|
|
20
20
|
- ✅ Enhanced issue creation, retrieval, and updating
|
|
@@ -32,7 +32,6 @@ pip install python-jira-plus
|
|
|
32
32
|
## Requirements
|
|
33
33
|
- Python 3.12+
|
|
34
34
|
- `jira` package
|
|
35
|
-
- `retrying` package
|
|
36
35
|
- `custom-python-logger` package
|
|
37
36
|
|
|
38
37
|
---
|
|
@@ -47,13 +46,26 @@ JIRA_TOKEN=your_jira_api_token
|
|
|
47
46
|
JIRA_BASE_URL=your-instance.atlassian.net # Only used if base_url is not provided to constructor
|
|
48
47
|
```
|
|
49
48
|
|
|
49
|
+
## Error handling
|
|
50
|
+
By default, write helpers (`delete_issue`, `create_link`, `transition_issue`, `upload_attachment`,
|
|
51
|
+
`update_issue`) and `get_issue_by_key` log the error and return `False`/`None`. Pass
|
|
52
|
+
`raise_on_error=True` to the constructor to get the original exception instead:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
jira_client = JiraCloud(raise_on_error=True)
|
|
56
|
+
jira_client.get_issue_by_key("PROJ-404") # raises JIRAError instead of returning None
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Validation problems (`ValueError`) and failed searches (`JIRAError`) are always raised.
|
|
60
|
+
Rate limits (429) and 503 are retried by `jira-python`'s own session, not by this package.
|
|
61
|
+
|
|
50
62
|
## Examples
|
|
51
63
|
|
|
52
64
|
### Creating an Issue with Custom Fields
|
|
53
65
|
```python
|
|
54
|
-
from python_jira_plus
|
|
66
|
+
from python_jira_plus import JiraCloud # or JiraOnPremise
|
|
55
67
|
|
|
56
|
-
jira_client =
|
|
68
|
+
jira_client = JiraCloud()
|
|
57
69
|
issue = jira_client.create_issue(
|
|
58
70
|
project_key="PROJ",
|
|
59
71
|
summary="Implement new feature",
|
|
@@ -68,12 +80,13 @@ issue = jira_client.create_issue(
|
|
|
68
80
|
|
|
69
81
|
### Searching for Issues
|
|
70
82
|
```python
|
|
71
|
-
from python_jira_plus
|
|
83
|
+
from python_jira_plus import JiraCloud
|
|
72
84
|
|
|
73
|
-
jira_client =
|
|
85
|
+
jira_client = JiraCloud()
|
|
74
86
|
issues = jira_client.get_objects_by_query(
|
|
75
87
|
query="project = PROJ AND status = 'In Progress' ORDER BY created DESC",
|
|
76
|
-
max_results=50,
|
|
88
|
+
max_results=50, # page size
|
|
89
|
+
limit=200, # optional cap on the total number of issues returned
|
|
77
90
|
specific_fields=["summary", "status", "assignee"],
|
|
78
91
|
json_result=False
|
|
79
92
|
)
|
|
@@ -84,9 +97,9 @@ for issue in issues:
|
|
|
84
97
|
|
|
85
98
|
### Updating an Issue
|
|
86
99
|
```python
|
|
87
|
-
from python_jira_plus
|
|
100
|
+
from python_jira_plus import JiraCloud
|
|
88
101
|
|
|
89
|
-
jira_client =
|
|
102
|
+
jira_client = JiraCloud()
|
|
90
103
|
issue = jira_client.get_issue_by_key(key="PROJ-123", json_result=False)
|
|
91
104
|
|
|
92
105
|
fields_to_update = {
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-jira-plus"
|
|
7
|
-
version = "
|
|
7
|
+
version = "4.1.0"
|
|
8
8
|
description = "Enhanced Python client for JIRA with better error handling, pagination, and metadata validation."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -28,9 +28,10 @@ dependencies = [
|
|
|
28
28
|
"requests>=2.32.5",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
|
-
[
|
|
31
|
+
[dependency-groups]
|
|
32
32
|
dev = [
|
|
33
33
|
"pre-commit>=4.5.0",
|
|
34
|
+
"python-base-toolkit>=1.1.2",
|
|
34
35
|
"pytest>=9.0.1",
|
|
35
36
|
"setuptools>=80.9.0",
|
|
36
37
|
"wheel>=0.45.1",
|
|
@@ -5,14 +5,11 @@ An enhanced Python client for JIRA with better error handling,
|
|
|
5
5
|
pagination, metadata validation, and more.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from dotenv import load_dotenv
|
|
9
|
-
|
|
10
8
|
from python_jira_plus.describe_allowed_value import describe_allowed_value
|
|
11
9
|
from python_jira_plus.jira_base import JiraBase, UrlScheme
|
|
12
10
|
from python_jira_plus.jira_cloud import JiraCloud
|
|
13
11
|
from python_jira_plus.jira_on_premise import JiraOnPremise
|
|
14
|
-
|
|
15
|
-
load_dotenv()
|
|
12
|
+
from python_jira_plus.types import CreateField
|
|
16
13
|
|
|
17
14
|
BASIC_FIELDS = [
|
|
18
15
|
"created",
|
|
@@ -29,4 +26,12 @@ BASIC_FIELDS = [
|
|
|
29
26
|
"resolution",
|
|
30
27
|
"parent",
|
|
31
28
|
]
|
|
32
|
-
__all__ = [
|
|
29
|
+
__all__ = [
|
|
30
|
+
"JiraBase",
|
|
31
|
+
"JiraCloud",
|
|
32
|
+
"JiraOnPremise",
|
|
33
|
+
"UrlScheme",
|
|
34
|
+
"BASIC_FIELDS",
|
|
35
|
+
"describe_allowed_value",
|
|
36
|
+
"CreateField",
|
|
37
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import NamedTuple
|
|
3
|
+
|
|
4
|
+
LOGGER_NAME = "python_jira_plus"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FieldTypeCheck(NamedTuple):
|
|
8
|
+
python_types: type | tuple[type, ...]
|
|
9
|
+
label: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ALLOWED_VALUE_KEYS = ("name", "key", "value", "id")
|
|
13
|
+
SCHEME_PATTERN = re.compile(r"^(https?)://", re.IGNORECASE)
|
|
14
|
+
FIELD_TYPE_CHECKS = {
|
|
15
|
+
"string": FieldTypeCheck(str, "a string"),
|
|
16
|
+
"number": FieldTypeCheck((int, float), "a number"),
|
|
17
|
+
"boolean": FieldTypeCheck(bool, "a boolean"),
|
|
18
|
+
"array": FieldTypeCheck(list, "an array"),
|
|
19
|
+
"object": FieldTypeCheck(dict, "an object"),
|
|
20
|
+
"issuetype": FieldTypeCheck(dict, "an issue type"),
|
|
21
|
+
}
|
|
22
|
+
ISSUE_REF_PATTERN = re.compile(r"[A-Za-z0-9_]+(-[0-9]+)?")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from python_jira_plus.consts import ISSUE_REF_PATTERN
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def allowed_value_label(value: Any) -> str:
|
|
7
|
+
"""A readable label for one entry of a field's `allowedValues`."""
|
|
8
|
+
if isinstance(value, dict):
|
|
9
|
+
return str(value.get("name") or value.get("value") or value.get("key") or value.get("id"))
|
|
10
|
+
return str(value)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def check_issue_ref(issue_ref: Any) -> None:
|
|
14
|
+
"""Reject an issue key/id that could change the request path (jira-python does not escape it)."""
|
|
15
|
+
if isinstance(issue_ref, str) and not ISSUE_REF_PATTERN.fullmatch(issue_ref):
|
|
16
|
+
raise ValueError(f"Invalid issue key '{issue_ref}'. Expected a key like PROJ-123 or a numeric id")
|