python-jira-plus 1.0.14__tar.gz → 1.0.15__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jira-plus
3
- Version: 1.0.14
3
+ Version: 1.0.15
4
4
  Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
5
5
  Home-page: https://github.com/aviz92/python-jira-plus
6
6
  Author: Avi Zaguri
@@ -12,6 +12,7 @@ License-File: LICENSE
12
12
  Requires-Dist: setuptools
13
13
  Requires-Dist: wheel
14
14
  Requires-Dist: dotenv
15
+ Requires-Dist: pre-commit
15
16
  Requires-Dist: custom-python-logger
16
17
  Requires-Dist: pytest
17
18
  Requires-Dist: requests
@@ -1,5 +1,3 @@
1
- # python-jira-plus
2
-
3
1
  """
4
2
  Python JIRA Plus
5
3
 
@@ -8,26 +6,25 @@ pagination, metadata validation, and more.
8
6
  """
9
7
 
10
8
  from dotenv import load_dotenv
11
- load_dotenv()
12
9
 
13
- from python_jira_plus.jira_plus import JiraPlus, ServerType
14
10
  from python_jira_plus.describe_allowed_value import describe_allowed_value
11
+ from python_jira_plus.jira_plus import JiraPlus, ServerType
15
12
 
16
13
  load_dotenv()
17
14
 
18
15
  BASIC_FIELDS = [
19
- 'created',
20
- 'updated',
21
- 'issuetype',
22
- 'project',
23
- 'status',
24
- 'summary',
25
- 'description',
26
- 'reporter',
27
- 'assignee',
28
- 'priority',
29
- 'labels',
30
- 'resolution',
31
- 'parent'
16
+ "created",
17
+ "updated",
18
+ "issuetype",
19
+ "project",
20
+ "status",
21
+ "summary",
22
+ "description",
23
+ "reporter",
24
+ "assignee",
25
+ "priority",
26
+ "labels",
27
+ "resolution",
28
+ "parent",
32
29
  ]
33
- __all__ = ['JiraPlus', 'ServerType', 'BASIC_FIELDS']
30
+ __all__ = ["JiraPlus", "ServerType", "BASIC_FIELDS"]
@@ -1,42 +1,43 @@
1
1
  # describe_allowed_value.py
2
2
 
3
3
  from functools import singledispatch
4
+ from typing import Any
4
5
 
5
6
 
6
7
  @singledispatch
7
- def describe_allowed_value(value, allowed_values: set = None):
8
+ def describe_allowed_value(value: Any, allowed_values: set = None) -> bool:
8
9
  raise NotImplementedError(f"cannot describe this object of type {type(value)}")
9
10
 
10
11
 
11
12
  @describe_allowed_value.register
12
- def _(value: int, allowed_values: list = None):
13
+ def _(value: int, allowed_values: list = None) -> bool:
13
14
  return value == allowed_values
14
15
 
15
16
 
16
17
  @describe_allowed_value.register
17
- def _(value: float, allowed_values: list = None):
18
+ def _(value: float, allowed_values: list = None) -> bool:
18
19
  return value == allowed_values
19
20
 
20
21
 
21
22
  @describe_allowed_value.register
22
- def _(value: str, allowed_values: list = None):
23
+ def _(value: str, allowed_values: list = None) -> bool:
23
24
  return value == allowed_values
24
25
 
25
26
 
26
27
  @describe_allowed_value.register
27
- def _(value: list, allowed_values: list = None):
28
- if all(isinstance(v, (str, int, float)) for v in value):
28
+ def _(value: list, allowed_values: list = None) -> bool:
29
+ if all(isinstance(v, str | int | float) for v in value):
29
30
  return any(v in allowed_values for v in value)
30
31
  if all(isinstance(v, dict) for v in value):
31
- return any([list(v.values())[0] in allowed_values for v in value])
32
+ return any(list(v.values())[0] in allowed_values for v in value)
32
33
  return False
33
34
 
34
35
 
35
36
  @describe_allowed_value.register
36
- def _(value: set, allowed_values: list = None):
37
+ def _(value: set, allowed_values: list = None) -> bool:
37
38
  return any(v in allowed_values for v in value)
38
39
 
39
40
 
40
41
  @describe_allowed_value.register
41
- def _(value: dict, allowed_values: list = None):
42
+ def _(value: dict, allowed_values: list = None) -> bool:
42
43
  return any(v in allowed_values for v in value.values())
@@ -5,10 +5,11 @@ import logging
5
5
  import os
6
6
  import time
7
7
  from enum import Enum
8
- from typing import Any, Union, Optional
8
+ from typing import Any
9
+
9
10
  import requests
10
11
  from custom_python_logger import get_logger
11
- from jira import JIRA, JIRAError, Issue
12
+ from jira import JIRA, Issue, JIRAError
12
13
  from jira.client import ResultList
13
14
  from retrying import retry
14
15
 
@@ -16,36 +17,37 @@ from python_jira_plus.describe_allowed_value import describe_allowed_value
16
17
 
17
18
 
18
19
  class UrlScheme(Enum):
19
- HTTP = 'http'
20
- HTTPS = 'https'
20
+ HTTP = "http"
21
+ HTTPS = "https"
21
22
 
22
23
 
23
24
  class ServerType(Enum):
24
- CLOUD = 'cloud'
25
- ON_PREMISE = 'on_premise'
25
+ CLOUD = "cloud"
26
+ ON_PREMISE = "on_premise"
26
27
 
27
28
 
28
29
  class JiraPlus:
29
30
  def __init__(
30
- self, base_url: Optional[str] = None,
31
+ self,
32
+ base_url: str | None = None,
31
33
  server_type: ServerType = ServerType.CLOUD,
32
34
  urllib3_log_level: int = logging.WARNING,
33
- jira_username: Optional[str] = None,
34
- jira_token: Optional[str] = None, # Use API token (not PAT)
35
+ jira_username: str | None = None,
36
+ jira_token: str | None = None, # Use API token (not PAT)
35
37
  verify_ssl: bool = True,
36
38
  sso: bool = False,
37
- url_scheme: UrlScheme = UrlScheme.HTTPS
39
+ url_scheme: UrlScheme = UrlScheme.HTTPS,
38
40
  ) -> None:
39
41
  self.logger = get_logger(self.__class__.__name__)
40
42
  get_logger("urllib3").setLevel(urllib3_log_level)
41
43
 
42
- self.jira_username = jira_username or os.getenv('JIRA_USER_NAME')
43
- self.jira_token = jira_token or os.getenv('JIRA_TOKEN')
44
- self.base_url = base_url or os.getenv('JIRA_BASE_URL')
44
+ self.jira_username = jira_username or os.getenv("JIRA_USER_NAME")
45
+ self.jira_token = jira_token or os.getenv("JIRA_TOKEN")
46
+ self.base_url = base_url or os.getenv("JIRA_BASE_URL")
45
47
  self.url_scheme = url_scheme.value
46
48
 
47
49
  self.server_type = server_type
48
- self.server = f'{self.url_scheme}://{self.base_url}'
50
+ self.server = f"{self.url_scheme}://{self.base_url}"
49
51
  self.verify_ssl = verify_ssl
50
52
  self.sso = sso
51
53
 
@@ -53,26 +55,23 @@ class JiraPlus:
53
55
  self.check_client_connection()
54
56
 
55
57
  @retry(stop_max_attempt_number=3, wait_fixed=180000)
56
- def create_connection(self, timeout=580) -> Optional[JIRA]:
57
- if self.server_type == ServerType.ON_PREMISE and not self.sso:
58
+ def create_connection(self, timeout: int = 580) -> JIRA | None:
59
+ if self.server_type == ServerType.ON_PREMISE and not self.sso: # pylint: disable=W0160:
58
60
  jira_client = JIRA(
59
- token_auth=self.jira_token,
60
- options={'server': self.server, 'verify': self.verify_ssl},
61
- timeout=timeout
61
+ token_auth=self.jira_token, options={"server": self.server, "verify": self.verify_ssl}, timeout=timeout
62
62
  )
63
63
  else:
64
64
  jira_client = JIRA(
65
65
  basic_auth=(self.jira_username, self.jira_token),
66
- options={'server': self.server, 'verify': self.verify_ssl},
67
- timeout=timeout
66
+ options={"server": self.server, "verify": self.verify_ssl},
67
+ timeout=timeout,
68
68
  )
69
69
 
70
70
  if jira_client.projects():
71
- self.logger.info(f"Jira Connection Successful")
71
+ self.logger.info("Jira Connection Successful")
72
72
  return jira_client
73
- else:
74
- self.logger.error(f"Jira Connection Failed")
75
- raise JIRAError("Jira Connection Failed")
73
+ self.logger.error("Jira Connection Failed")
74
+ raise JIRAError("Jira Connection Failed")
76
75
 
77
76
  def close_connection(self) -> None:
78
77
  try:
@@ -88,9 +87,8 @@ class JiraPlus:
88
87
  try:
89
88
  if self.jira_client.projects():
90
89
  return True
91
- else:
92
- self.logger.info("Jira client is closed")
93
- return False
90
+ self.logger.info("Jira client is closed")
91
+ return False
94
92
  except JIRAError as err:
95
93
  self.logger.exception(f"JIRAError \n{err}")
96
94
  return False
@@ -99,10 +97,7 @@ class JiraPlus:
99
97
  return False
100
98
 
101
99
  def get_issue_by_key(
102
- self,
103
- key: str,
104
- specific_fields: str | list[str] = '*all',
105
- json_result: bool = True
100
+ self, key: str, specific_fields: str | list[str] = "*all", json_result: bool = True
106
101
  ) -> None | Issue | dict[str, Any]:
107
102
  try:
108
103
  jira_issue = self.jira_client.issue(id=key, fields=specific_fields)
@@ -120,10 +115,10 @@ class JiraPlus:
120
115
  self,
121
116
  query: str,
122
117
  max_results: int = 5000,
123
- specific_fields: Union[str, list[str]] = "*all",
118
+ specific_fields: str | list[str] = "*all",
124
119
  jira_err_count: int = 3,
125
120
  json_result: bool = True,
126
- ) -> Optional[Union[ResultList[Issue], dict]]:
121
+ ) -> ResultList[Issue] | dict | None:
127
122
  return self._paginate_query(
128
123
  query=query,
129
124
  max_results=max_results,
@@ -136,14 +131,14 @@ class JiraPlus:
136
131
  self,
137
132
  query: str,
138
133
  max_results: int,
139
- specific_fields: Union[str, list[str]],
134
+ specific_fields: str | list[str],
140
135
  jira_err_limit: int,
141
- json_result: bool
142
- ) -> Union[ResultList[Issue] | list]:
136
+ json_result: bool,
137
+ ) -> ResultList[Issue] | list:
143
138
  start_at = 0
144
139
  all_issues = []
145
140
  retries = 0
146
- while True:
141
+ while True: # pylint: disable=W0149
147
142
  try:
148
143
  self.logger.debug(f"Fetching issues from startAt={start_at}")
149
144
  page = self.jira_client.search_issues(
@@ -151,11 +146,11 @@ class JiraPlus:
151
146
  startAt=start_at,
152
147
  maxResults=max_results,
153
148
  fields=specific_fields,
154
- json_result=json_result
149
+ json_result=json_result,
155
150
  )
156
- all_issues += page['issues'] if json_result else page
151
+ all_issues += page["issues"] if json_result else page
157
152
  if json_result:
158
- start_at += page['maxResults']
153
+ start_at += page["maxResults"]
159
154
  else:
160
155
  start_at += page.maxResults
161
156
  if start_at >= max_results:
@@ -164,7 +159,7 @@ class JiraPlus:
164
159
  except JIRAError as err:
165
160
  if err.status_code == 400:
166
161
  self.logger.error(f"Bad Request: {err.text}")
167
- raise JIRAError(f"Bad Request: {err.text}")
162
+ raise JIRAError(f"Bad Request: {err.text}") from err
168
163
  retries += 1
169
164
  self.logger.warning(f"Retry {retries}/{jira_err_limit} after JIRAError: {err}")
170
165
  if retries >= jira_err_limit:
@@ -175,19 +170,13 @@ class JiraPlus:
175
170
 
176
171
  def _fetch_metadata(self, project_key: str, issue_type: str) -> tuple[dict, dict]:
177
172
  meta = self.jira_client.createmeta(
178
- projectKeys=project_key,
179
- issuetypeNames=issue_type,
180
- expand="projects.issuetypes.fields"
173
+ projectKeys=project_key, issuetypeNames=issue_type, expand="projects.issuetypes.fields"
181
174
  )
182
175
 
183
- project_meta = next((p for p in meta['projects'] if p['key'] == project_key), None)
184
- if not project_meta:
176
+ if not (project_meta := next((p for p in meta["projects"] if p["key"] == project_key), None)):
185
177
  raise ValueError(f"Project {project_key} not found in metadata")
186
178
 
187
- issue_meta = next(
188
- (it for it in project_meta['issuetypes'] if it['name'].lower() == issue_type.lower()),
189
- None
190
- )
179
+ issue_meta = next((it for it in project_meta["issuetypes"] if it["name"].lower() == issue_type.lower()), None)
191
180
  if not issue_meta:
192
181
  raise ValueError(f"Issue type '{issue_type}' not found in project '{project_key}'")
193
182
  return meta, issue_meta
@@ -197,47 +186,44 @@ class JiraPlus:
197
186
  project_key: str,
198
187
  issue_type: str,
199
188
  field_id_or_name: str,
200
- ) -> Optional[list]:
189
+ ) -> list | None:
201
190
  """
202
191
  Returns the allowed values for a specific field in a project + issue type context.
203
192
  """
204
- fields_metadata = self.get_project_fields_metadata(
205
- project_key=project_key,
206
- issue_type=issue_type
207
- )
193
+ fields_metadata = self.get_project_fields_metadata(project_key=project_key, issue_type=issue_type)
208
194
 
209
195
  # Try to find by ID or fallback to case-insensitive name
210
- field = fields_metadata.get(field_id_or_name)
211
- if not field:
196
+ if not (field := fields_metadata.get(field_id_or_name)):
212
197
  # Try matching by name (case-insensitive)
213
- for fid, fmeta in fields_metadata.items():
214
- if fmeta['name'].lower() == field_id_or_name.lower():
198
+ for _, fmeta in fields_metadata.items():
199
+ if fmeta["name"].lower() == field_id_or_name.lower():
215
200
  field = fmeta
216
201
  break
217
202
 
218
203
  if not field:
219
204
  raise ValueError(
220
- f"Field '{field_id_or_name}' not found in project '{project_key}' with issue type '{issue_type}'")
205
+ f"Field '{field_id_or_name}' not found in project '{project_key}' with issue type '{issue_type}'"
206
+ )
221
207
 
222
- allowed = field.get('allowedValues')
223
- if not allowed:
208
+ if not (allowed := field.get("allowedValues")):
224
209
  self.logger.info(f"No allowed values found for field '{field_id_or_name}'")
225
210
  return None
226
211
 
227
212
  return allowed
228
213
 
229
- def _is_value_allowed(self, value: Any, allowed_values: list[Any]) -> bool:
230
- allowed_names = {av.get('name') for av in allowed_values}
231
- allowed_keys = {av.get('key') for av in allowed_values}
214
+ @staticmethod
215
+ def _is_value_allowed(value: Any, allowed_values: list[Any]) -> bool:
216
+ allowed_names = {av.get("name") for av in allowed_values}
217
+ allowed_keys = {av.get("key") for av in allowed_values}
232
218
  allowed_values = allowed_names | allowed_keys
233
219
  return describe_allowed_value(value, allowed_values=allowed_values)
234
220
 
235
- def get_server_version(self):
236
- return tuple(self.jira_client.server_info()['versionNumbers'])
221
+ def get_server_version(self) -> tuple[int, ...]:
222
+ return tuple(self.jira_client.server_info()["versionNumbers"])
237
223
 
238
224
  def check_server_createmeta_compatibility(self) -> bool:
239
225
  server_version = self.get_server_version()
240
- if self.server_type == ServerType.ON_PREMISE and ''.join(str(x) for x in server_version) > '9.0.0':
226
+ if self.server_type == ServerType.ON_PREMISE and "".join(str(x) for x in server_version) > "9.0.0":
241
227
  self.logger.warning(
242
228
  f"JIRA server version {server_version} is below the minimum required version (9.0.0). "
243
229
  "Some features may not work as expected."
@@ -247,49 +233,43 @@ class JiraPlus:
247
233
 
248
234
  def get_project_fields_metadata(self, project_key: str, issue_type: str) -> dict:
249
235
  if self.check_server_createmeta_compatibility():
250
- meta, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
251
- return issue_meta['fields']
252
- else:
253
- _project_issue_types = self.jira_client.project_issue_types(project_key)
254
- _issue_types = {t.name: t for t in _project_issue_types}
255
- field_list = self.jira_client.project_issue_fields(project_key, _issue_types[issue_type].id)
256
- return {f.fieldId: f.raw for f in field_list}
257
-
258
- def validate_fields(
259
- self,
260
- project_key: str,
261
- issue_type: str,
262
- fields: Optional[dict] = None
236
+ _, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
237
+ return issue_meta["fields"]
238
+ _project_issue_types = self.jira_client.project_issue_types(project_key)
239
+ _issue_types = {t.name: t for t in _project_issue_types}
240
+ field_list = self.jira_client.project_issue_fields(project_key, _issue_types[issue_type].id)
241
+ return {f.fieldId: f.raw for f in field_list}
242
+
243
+ def validate_fields( # pylint: disable=R1260
244
+ self, project_key: str, issue_type: str, fields: dict | None = None
263
245
  ) -> None:
264
- fields_metadata = self.get_project_fields_metadata(
265
- project_key=project_key,
266
- issue_type=issue_type
267
- )
246
+ fields_metadata = self.get_project_fields_metadata(project_key=project_key, issue_type=issue_type)
268
247
  for custom_field, value in fields.items():
269
248
  if custom_field not in fields_metadata:
270
249
  raise ValueError(
271
250
  f'Invalid field(s) for project "{project_key}", issue type "{issue_type}": "{custom_field}"'
272
251
  )
273
252
 
274
- if allowed_value := fields_metadata[custom_field].get('allowedValues'):
253
+ if allowed_value := fields_metadata[custom_field].get("allowedValues"):
275
254
  if not self._is_value_allowed(value=value, allowed_values=allowed_value):
276
255
  raise ValueError(
277
- f"Invalid value '{value}' for field '{custom_field}' in project '{project_key}' with issue type '{issue_type}'."
256
+ f"Invalid value '{value}' for field '{custom_field}' in project '{project_key}' "
257
+ f"with issue type '{issue_type}'."
278
258
  f"The allowed values are: {json.dumps(allowed_value, indent=4, sort_keys=True)}"
279
259
  )
280
260
 
281
- field_type = fields_metadata[custom_field]['schema']['type']
282
- if field_type == 'string' and not isinstance(value, str):
261
+ field_type = fields_metadata[custom_field]["schema"]["type"]
262
+ if field_type == "string" and not isinstance(value, str):
283
263
  raise ValueError(f"Field '{custom_field}' must be a string")
284
- elif field_type == 'number' and not isinstance(value, (int, float)):
264
+ if field_type == "number" and not isinstance(value, int | float):
285
265
  raise ValueError(f"Field '{custom_field}' must be a number")
286
- elif field_type == 'boolean' and not isinstance(value, bool):
266
+ if field_type == "boolean" and not isinstance(value, bool):
287
267
  raise ValueError(f"Field '{custom_field}' must be a boolean")
288
- elif field_type == 'array' and not isinstance(value, list):
268
+ if field_type == "array" and not isinstance(value, list):
289
269
  raise ValueError(f"Field '{custom_field}' must be an array")
290
- elif field_type == 'object' and not isinstance(value, dict):
270
+ if field_type == "object" and not isinstance(value, dict):
291
271
  raise ValueError(f"Field '{custom_field}' must be an object")
292
- elif field_type == 'issuetype' and not isinstance(value, dict):
272
+ if field_type == "issuetype" and not isinstance(value, dict):
293
273
  raise ValueError(f"Field '{custom_field}' must be an issue type")
294
274
  # Add more field type checks as needed
295
275
 
@@ -299,9 +279,9 @@ class JiraPlus:
299
279
  summary: str,
300
280
  description: str,
301
281
  issue_type: str,
302
- assignee: Optional[str] = None,
303
- custom_fields: Optional[dict] = None,
304
- ) -> Optional[Issue]:
282
+ assignee: str | None = None,
283
+ custom_fields: dict | None = None,
284
+ ) -> Issue | None:
305
285
  issue_fields = {
306
286
  "project": {"key": project_key},
307
287
  "issuetype": {"name": issue_type},
@@ -309,8 +289,9 @@ class JiraPlus:
309
289
  "description": description,
310
290
  }
311
291
  if assignee:
312
- issue_fields["assignee"] = {"accountId": assignee} if self.server_type == ServerType.CLOUD else {
313
- "name": assignee}
292
+ issue_fields["assignee"] = (
293
+ {"accountId": assignee} if self.server_type == ServerType.CLOUD else {"name": assignee}
294
+ )
314
295
 
315
296
  if custom_fields:
316
297
  issue_fields = issue_fields | custom_fields
@@ -320,17 +301,11 @@ class JiraPlus:
320
301
  self.logger.info(f"Issue {issue.key} created successfully.")
321
302
  return issue
322
303
 
323
- def update_issue(
324
- self,
325
- issue_key: str,
326
- fields_to_update: dict
327
- ) -> Optional[Issue]:
304
+ def update_issue(self, issue_key: str, fields_to_update: dict) -> Issue | None:
328
305
  try:
329
306
  issue = self.jira_client.issue(issue_key)
330
307
  self.validate_fields(
331
- project_key=issue.fields.project.key,
332
- issue_type=issue.fields.issuetype.name,
333
- fields=fields_to_update
308
+ project_key=issue.fields.project.key, issue_type=issue.fields.issuetype.name, fields=fields_to_update
334
309
  )
335
310
 
336
311
  issue.update(fields=fields_to_update)
@@ -399,12 +374,12 @@ class JiraPlus:
399
374
  self,
400
375
  issue_key: str,
401
376
  transition_name: str,
402
- fields: Optional[dict] = None,
403
- comment: Optional[str] = None,
377
+ fields: dict | None = None,
378
+ comment: str | None = None,
404
379
  ) -> bool:
405
380
  try:
406
381
  transitions = self.jira_client.transitions(issue_key)
407
- transition_names = {t['name'] for t in transitions}
382
+ transition_names = {t["name"] for t in transitions}
408
383
  if transition_name not in transition_names:
409
384
  self.logger.error(
410
385
  f"Transition '{transition_name}' not found for issue {issue_key}. "
@@ -412,19 +387,13 @@ class JiraPlus:
412
387
  )
413
388
  return False
414
389
 
415
- transition_id = next(
416
- (t['id'] for t in transitions if t['name'].lower() == transition_name.lower()),
417
- None
418
- )
390
+ transition_id = next((t["id"] for t in transitions if t["name"].lower() == transition_name.lower()), None)
419
391
  if not transition_id:
420
392
  self.logger.error(f"Transition '{transition_name}' not found for issue {issue_key}")
421
393
  return False
422
394
 
423
395
  self.jira_client.transition_issue(
424
- issue=issue_key,
425
- transition=transition_id,
426
- fields=fields or {},
427
- comment=comment or None
396
+ issue=issue_key, transition=transition_id, fields=fields or {}, comment=comment or None
428
397
  )
429
398
  self.logger.info(f"Issue {issue_key} transitioned to '{transition_name}'")
430
399
  return True
@@ -436,15 +405,16 @@ class JiraPlus:
436
405
  return False
437
406
 
438
407
  def get_account_data(self, user_mail: str) -> list[dict]:
439
- jira_rst_url = \
440
- f'https://{self.jira_username}:{self.jira_token}@{self.base_url}/rest/api/{3}/user/search?query={user_mail}'
408
+ jira_rst_url = (
409
+ f"https://{self.jira_username}:{self.jira_token}@{self.base_url}/rest/api/{3}/user/search?query={user_mail}"
410
+ )
441
411
  response = requests.get(jira_rst_url)
442
412
  return response.json()
443
413
 
444
414
  def get_account_id(self, user_mail: str) -> str:
445
415
  data = self.get_account_data(user_mail=user_mail)
446
- return data[0]['accountId']
416
+ return data[0]["accountId"]
447
417
 
448
418
  def get_account_display_name(self, user_display_name: str) -> str:
449
419
  data = self.get_account_data(user_mail=user_display_name)
450
- return data[0]['displayName']
420
+ return data[0]["displayName"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jira-plus
3
- Version: 1.0.14
3
+ Version: 1.0.15
4
4
  Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
5
5
  Home-page: https://github.com/aviz92/python-jira-plus
6
6
  Author: Avi Zaguri
@@ -12,6 +12,7 @@ License-File: LICENSE
12
12
  Requires-Dist: setuptools
13
13
  Requires-Dist: wheel
14
14
  Requires-Dist: dotenv
15
+ Requires-Dist: pre-commit
15
16
  Requires-Dist: custom-python-logger
16
17
  Requires-Dist: pytest
17
18
  Requires-Dist: requests
@@ -1,6 +1,7 @@
1
1
  setuptools
2
2
  wheel
3
3
  dotenv
4
+ pre-commit
4
5
  custom-python-logger
5
6
  pytest
6
7
  requests
@@ -1,6 +1,7 @@
1
1
  setuptools
2
2
  wheel
3
3
  dotenv
4
+ pre-commit
4
5
  custom-python-logger
5
6
  pytest
6
7
  requests
@@ -0,0 +1,35 @@
1
+ from setuptools import find_packages, setup
2
+
3
+ package_version = "1.0.15"
4
+
5
+ package_name = "python-jira-plus"
6
+ package_description = "Enhanced Python client for JIRA with better error handling, pagination, and metadata validation."
7
+
8
+ package_name_ = package_name.replace("-", "_")
9
+ package_long_description_content_type = "text/markdown"
10
+ package_url = f"https://github.com/aviz92/{package_name}"
11
+ package_python_requires = ">=3.11"
12
+ package_author = "Avi Zaguri"
13
+
14
+ with open("requirements.txt") as file:
15
+ package_install_requires = [line.strip() for line in file.readlines() if line.strip() and not line.startswith("#")]
16
+
17
+ with open("README.md") as file:
18
+ package_long_description = file.read()
19
+
20
+ setup(
21
+ name=package_name,
22
+ version=package_version,
23
+ packages=find_packages(include=[package_name_, f"{package_name_}.*"]),
24
+ install_requires=package_install_requires,
25
+ author=package_author,
26
+ author_email="",
27
+ description=package_description,
28
+ long_description=package_long_description,
29
+ long_description_content_type=package_long_description_content_type,
30
+ url=package_url,
31
+ project_urls={
32
+ "Repository": package_url,
33
+ },
34
+ python_requires=package_python_requires,
35
+ )
@@ -1,37 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- package_version = '1.0.14'
4
-
5
- package_name = 'python-jira-plus'
6
- package_description = 'Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.'
7
-
8
- package_name_ = package_name.replace('-', '_')
9
- package_long_description_content_type = 'text/markdown'
10
- package_url = f'https://github.com/aviz92/{package_name}'
11
- package_python_requires = '>=3.11'
12
- package_author = 'Avi Zaguri'
13
-
14
- with open('requirements.txt', 'r') as file:
15
- package_install_requires = [
16
- line.strip() for line in file.readlines() if line.strip() and not line.startswith('#')
17
- ]
18
-
19
- with open('README.md', 'r') as file:
20
- package_long_description = file.read()
21
-
22
- setup(
23
- name=package_name,
24
- version=package_version,
25
- packages=find_packages(include=[package_name_, f'{package_name_}.*']),
26
- install_requires=package_install_requires,
27
- author=package_author,
28
- author_email='',
29
- description=package_description,
30
- long_description=package_long_description,
31
- long_description_content_type=package_long_description_content_type,
32
- url=package_url,
33
- project_urls={
34
- 'Repository': package_url,
35
- },
36
- python_requires=package_python_requires,
37
- )