bugdantic 0.4.1__tar.gz → 0.4.3__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.
- {bugdantic-0.4.1 → bugdantic-0.4.3}/PKG-INFO +5 -4
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic/bugzilla.py +27 -16
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic/userstory.py +4 -4
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic.egg-info/PKG-INFO +5 -4
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic.egg-info/requires.txt +4 -3
- {bugdantic-0.4.1 → bugdantic-0.4.3}/pyproject.toml +11 -4
- {bugdantic-0.4.1 → bugdantic-0.4.3}/LICENSE +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/README.md +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic/__init__.py +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic/py.typed +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic.egg-info/SOURCES.txt +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic.egg-info/dependency_links.txt +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/bugdantic.egg-info/top_level.txt +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/setup.cfg +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/test/test_bugzilla.py +0 -0
- {bugdantic-0.4.1 → bugdantic-0.4.3}/test/test_userstory.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bugdantic
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Bugzilla client based on pydantic
|
|
5
5
|
Author-email: James Graham <james@hoppipolla.co.uk>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -11,8 +11,9 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: httpx[http2]>=0.28.1
|
|
12
12
|
Requires-Dist: pydantic>=2.11.3
|
|
13
13
|
Provides-Extra: test
|
|
14
|
-
Requires-Dist: mypy==
|
|
15
|
-
Requires-Dist: ruff==0.
|
|
16
|
-
Requires-Dist: pytest==
|
|
14
|
+
Requires-Dist: mypy==2.3.0; extra == "test"
|
|
15
|
+
Requires-Dist: ruff==0.16.2; extra == "test"
|
|
16
|
+
Requires-Dist: pytest==9.1.1; extra == "test"
|
|
17
17
|
Requires-Dist: pytest-ruff==0.5; extra == "test"
|
|
18
|
+
Requires-Dist: ty==0.0.71; extra == "test"
|
|
18
19
|
Dynamic: license-file
|
|
@@ -2,15 +2,14 @@ import base64
|
|
|
2
2
|
import enum
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
-
|
|
5
|
+
import time
|
|
6
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
7
|
+
from dataclasses import dataclass, field
|
|
6
8
|
from datetime import datetime
|
|
7
9
|
from typing import (
|
|
8
10
|
Any,
|
|
9
11
|
Generic,
|
|
10
|
-
Mapping,
|
|
11
|
-
MutableMapping,
|
|
12
12
|
Optional,
|
|
13
|
-
Sequence,
|
|
14
13
|
Self,
|
|
15
14
|
TypeVar,
|
|
16
15
|
cast,
|
|
@@ -438,6 +437,7 @@ class BugzillaConfig:
|
|
|
438
437
|
allow_writes: bool = False
|
|
439
438
|
# Number of times to retry a request if there's a 503 error
|
|
440
439
|
max_retries: int = 1
|
|
440
|
+
retry_status_codes: set[int] = field(default_factory=lambda: {502, 503})
|
|
441
441
|
|
|
442
442
|
|
|
443
443
|
BugType = TypeVar("BugType", bound=BaseModel)
|
|
@@ -495,33 +495,44 @@ class Bugzilla:
|
|
|
495
495
|
if exclude_fields is not None:
|
|
496
496
|
params["exclude_fields"] = ",".join(exclude_fields)
|
|
497
497
|
|
|
498
|
-
|
|
499
|
-
path = path[1:]
|
|
498
|
+
path = path.removeprefix("/")
|
|
500
499
|
|
|
501
500
|
url = urljoin(self.config.base_url, f"/rest/{path}")
|
|
502
|
-
|
|
501
|
+
logging.debug("Fetching from bugzilla: %s", url)
|
|
503
502
|
if self.config.allow_writes or method in {"GET", "OPTIONS", "HEAD"}:
|
|
504
503
|
retry = 0
|
|
505
504
|
if self.config.max_retries < 0:
|
|
506
505
|
raise ValueError("max_retries must be at least 0")
|
|
507
506
|
response = None
|
|
508
507
|
while retry <= self.config.max_retries:
|
|
508
|
+
if retry != 0:
|
|
509
|
+
wait_time = 2**retry
|
|
510
|
+
logging.debug(
|
|
511
|
+
"Request failed, retrying %s/%s, sleeping %ss",
|
|
512
|
+
retry,
|
|
513
|
+
self.config.max_retries,
|
|
514
|
+
wait_time,
|
|
515
|
+
)
|
|
516
|
+
time.sleep(wait_time)
|
|
509
517
|
retry += 1
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
518
|
+
try:
|
|
519
|
+
response = self.client.request(
|
|
520
|
+
method, url, params=params, headers=headers, json=json_body
|
|
521
|
+
)
|
|
522
|
+
except httpx.ReadTimeout:
|
|
523
|
+
continue
|
|
524
|
+
if response.status_code not in self.config.retry_status_codes:
|
|
514
525
|
break
|
|
515
526
|
assert response is not None
|
|
516
527
|
try:
|
|
517
528
|
response.raise_for_status()
|
|
518
|
-
except Exception
|
|
529
|
+
except Exception:
|
|
519
530
|
msg = "Request failed"
|
|
520
531
|
json_resp = response.json()
|
|
521
532
|
if json_resp:
|
|
522
533
|
msg += f"\n{json_resp.get('message')}"
|
|
523
534
|
logging.error(msg)
|
|
524
|
-
raise
|
|
535
|
+
raise
|
|
525
536
|
return response.json()
|
|
526
537
|
else:
|
|
527
538
|
logging.info(f"""Not updating, would send {method} request to {path} with body:
|
|
@@ -592,9 +603,9 @@ class Bugzilla:
|
|
|
592
603
|
bug_ids: Sequence[int],
|
|
593
604
|
bug_type: type[BugType],
|
|
594
605
|
page_size: int = 100,
|
|
595
|
-
) -> list[
|
|
606
|
+
) -> list[BugType]:
|
|
596
607
|
"""Get multiple bugs specified by id"""
|
|
597
|
-
results: list[
|
|
608
|
+
results: list[BugType] = []
|
|
598
609
|
for bug_ids_chunk in [
|
|
599
610
|
bug_ids[n : n + page_size] for n in range(0, len(bug_ids), page_size)
|
|
600
611
|
]:
|
|
@@ -675,7 +686,7 @@ class Bugzilla:
|
|
|
675
686
|
|
|
676
687
|
def search_as(
|
|
677
688
|
self, query: QueryParams, bug_type: type[BugType], page_size: int = 100
|
|
678
|
-
):
|
|
689
|
+
) -> list[BugType]:
|
|
679
690
|
include_fields = model_field_names(bug_type)
|
|
680
691
|
return self._search(bug_type, query, include_fields, page_size)
|
|
681
692
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Utility functions for working with cf_user_story fields that
|
|
2
2
|
are used to store key: value pair data"""
|
|
3
3
|
|
|
4
|
+
import re
|
|
4
5
|
from collections import defaultdict
|
|
5
|
-
from
|
|
6
|
+
from collections.abc import Iterator, Mapping
|
|
6
7
|
from dataclasses import dataclass
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
from typing import Iterator, Optional, Mapping, Self
|
|
8
|
+
from enum import IntEnum
|
|
9
|
+
from typing import Optional, Self
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def parse(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bugdantic
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Bugzilla client based on pydantic
|
|
5
5
|
Author-email: James Graham <james@hoppipolla.co.uk>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -11,8 +11,9 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: httpx[http2]>=0.28.1
|
|
12
12
|
Requires-Dist: pydantic>=2.11.3
|
|
13
13
|
Provides-Extra: test
|
|
14
|
-
Requires-Dist: mypy==
|
|
15
|
-
Requires-Dist: ruff==0.
|
|
16
|
-
Requires-Dist: pytest==
|
|
14
|
+
Requires-Dist: mypy==2.3.0; extra == "test"
|
|
15
|
+
Requires-Dist: ruff==0.16.2; extra == "test"
|
|
16
|
+
Requires-Dist: pytest==9.1.1; extra == "test"
|
|
17
17
|
Requires-Dist: pytest-ruff==0.5; extra == "test"
|
|
18
|
+
Requires-Dist: ty==0.0.71; extra == "test"
|
|
18
19
|
Dynamic: license-file
|
|
@@ -5,7 +5,7 @@ authors = [
|
|
|
5
5
|
{name = "James Graham", email = "james@hoppipolla.co.uk"}
|
|
6
6
|
]
|
|
7
7
|
|
|
8
|
-
version = "0.4.
|
|
8
|
+
version = "0.4.3"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
license = "MPL-2.0"
|
|
11
11
|
classifiers = [
|
|
@@ -20,8 +20,15 @@ dependencies = [
|
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
test = [
|
|
23
|
-
"mypy==
|
|
24
|
-
"ruff==0.
|
|
25
|
-
"pytest==
|
|
23
|
+
"mypy==2.3.0",
|
|
24
|
+
"ruff==0.16.2",
|
|
25
|
+
"pytest==9.1.1",
|
|
26
26
|
"pytest-ruff==0.5",
|
|
27
|
+
"ty==0.0.71",
|
|
27
28
|
]
|
|
29
|
+
|
|
30
|
+
[tool.mypy]
|
|
31
|
+
disallow_untyped_defs = true
|
|
32
|
+
|
|
33
|
+
[tool.ruff.lint]
|
|
34
|
+
ignore = ["UP045", "LOG015"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|