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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bugdantic
3
- Version: 0.4.1
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==1.18.2; extra == "test"
15
- Requires-Dist: ruff==0.14.1; extra == "test"
16
- Requires-Dist: pytest==8.4.2; extra == "test"
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
- from dataclasses import dataclass
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
- if path.startswith("/"):
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
- response = self.client.request(
511
- method, url, params=params, headers=headers, json=json_body
512
- )
513
- if response.status_code != 503:
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 as e:
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 e
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[Bug]:
606
+ ) -> list[BugType]:
596
607
  """Get multiple bugs specified by id"""
597
- results: list[Bug] = []
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 enum import IntEnum
6
+ from collections.abc import Iterator, Mapping
6
7
  from dataclasses import dataclass
7
-
8
- import re
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.1
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==1.18.2; extra == "test"
15
- Requires-Dist: ruff==0.14.1; extra == "test"
16
- Requires-Dist: pytest==8.4.2; extra == "test"
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,7 +2,8 @@ httpx[http2]>=0.28.1
2
2
  pydantic>=2.11.3
3
3
 
4
4
  [test]
5
- mypy==1.18.2
6
- ruff==0.14.1
7
- pytest==8.4.2
5
+ mypy==2.3.0
6
+ ruff==0.16.2
7
+ pytest==9.1.1
8
8
  pytest-ruff==0.5
9
+ ty==0.0.71
@@ -5,7 +5,7 @@ authors = [
5
5
  {name = "James Graham", email = "james@hoppipolla.co.uk"}
6
6
  ]
7
7
 
8
- version = "0.4.1"
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==1.18.2",
24
- "ruff==0.14.1",
25
- "pytest==8.4.2",
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