fluidattacks_gitlab_sdk 7.0.0__tar.gz → 8.0.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.
Files changed (98) hide show
  1. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/PKG-INFO +1 -1
  2. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/__init__.py +1 -1
  3. fluidattacks_gitlab_sdk-8.0.0/fluidattacks_gitlab_sdk/issues/_client/_core.py +17 -0
  4. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/_client/_decode.py +12 -11
  5. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +7 -8
  6. fluidattacks_gitlab_sdk-7.0.0/fluidattacks_gitlab_sdk/issues/_client/_updated_by.py → fluidattacks_gitlab_sdk-8.0.0/fluidattacks_gitlab_sdk/issues/_client/_gql_fields.py +35 -10
  7. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +9 -9
  8. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/core.py +1 -0
  9. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/arch/arch.py +3 -1
  10. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/issues/test_decode.py +12 -2
  11. fluidattacks_gitlab_sdk-8.0.0/tests/issues/test_gql_fields.py +70 -0
  12. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/uv.lock +34 -47
  13. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/.envrc +0 -0
  14. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/build/default.nix +0 -0
  15. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/build/filter.nix +0 -0
  16. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/flake.lock +0 -0
  17. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/flake.nix +0 -0
  18. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_decoders.py +0 -0
  19. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_gql_client/__init__.py +0 -0
  20. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_gql_client/_client.py +0 -0
  21. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_gql_client/_error.py +0 -0
  22. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_gql_client/_handlers.py +0 -0
  23. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_handlers.py +0 -0
  24. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_http_client/__init__.py +0 -0
  25. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_http_client/_client_1.py +0 -0
  26. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_http_client/_core.py +0 -0
  27. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/_logger.py +0 -0
  28. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/ids.py +0 -0
  29. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/__init__.py +0 -0
  30. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/issues/_client/__init__.py +0 -0
  31. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/jobs/__init__.py +0 -0
  32. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/jobs/_client.py +0 -0
  33. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/jobs/_decode.py +0 -0
  34. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/jobs/core.py +0 -0
  35. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/members/__init__.py +0 -0
  36. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/members/_client.py +0 -0
  37. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/members/_decode.py +0 -0
  38. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/members/core.py +0 -0
  39. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/merge_requests/__init__.py +0 -0
  40. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/merge_requests/_client.py +0 -0
  41. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/merge_requests/_decode.py +0 -0
  42. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/merge_requests/core.py +0 -0
  43. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/milestones/__init__.py +0 -0
  44. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/milestones/_client.py +0 -0
  45. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/milestones/_decode.py +0 -0
  46. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/milestones/core.py +0 -0
  47. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_approvals/__init__.py +0 -0
  48. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_approvals/_client.py +0 -0
  49. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_approvals/_decode.py +0 -0
  50. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_approvals/core.py +0 -0
  51. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_notes/__init__.py +0 -0
  52. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_notes/_client.py +0 -0
  53. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_notes/_core.py +0 -0
  54. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/mr_notes/_decode.py +0 -0
  55. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/py.typed +0 -0
  56. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/users/__init__.py +0 -0
  57. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/users/core.py +0 -0
  58. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/fluidattacks_gitlab_sdk/users/decode.py +0 -0
  59. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/mypy.ini +0 -0
  60. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/pyproject.toml +0 -0
  61. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/ruff.toml +0 -0
  62. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/__init__.py +0 -0
  63. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/arch/__init__.py +0 -0
  64. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/arch/test_arch.py +0 -0
  65. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/issues/__init__.py +0 -0
  66. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/issues/data.json +0 -0
  67. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/jobs/__init__.py +0 -0
  68. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/jobs/data.json +0 -0
  69. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/jobs/test_decode_job.py +0 -0
  70. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/members/__init__.py +0 -0
  71. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/members/data.json +0 -0
  72. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/members/test_decode_members.py +0 -0
  73. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/merge_request/__init__.py +0 -0
  74. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/merge_request/data.json +0 -0
  75. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/merge_request/data_mr_updates.json +0 -0
  76. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/merge_request/test_decode.py +0 -0
  77. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_approvals/__init__.py +0 -0
  78. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_approvals/data.json +0 -0
  79. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_approvals/decoder_test.py +0 -0
  80. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_notes/__init__.py +0 -0
  81. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_notes/data.json +0 -0
  82. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/mr_notes/decoder_test.py +0 -0
  83. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests/py.typed +0 -0
  84. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/__init__.py +0 -0
  85. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/_utils.py +0 -0
  86. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/issues/__init__.py +0 -0
  87. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/issues/test_issue_client.py +0 -0
  88. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/jobs/__init__.py +0 -0
  89. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/jobs/client_test.py +0 -0
  90. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/members/__init__.py +0 -0
  91. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/members/test_client_members.py +0 -0
  92. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/milestones/__init__.py +0 -0
  93. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/milestones/client_test.py +0 -0
  94. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/mr_approvals/__init__.py +0 -0
  95. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/mr_approvals/client_test.py +0 -0
  96. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/py.typed +0 -0
  97. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/test_gql_client.py +0 -0
  98. {fluidattacks_gitlab_sdk-7.0.0 → fluidattacks_gitlab_sdk-8.0.0}/tests_fx/test_mr.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluidattacks_gitlab_sdk
3
- Version: 7.0.0
3
+ Version: 8.0.0
4
4
  Summary: gitlab SDK
5
5
  Author-email: Product Team <development@fluidattacks.com>
6
6
  Requires-Python: >=3.13.14, <3.14
@@ -11,7 +11,7 @@ from ._logger import (
11
11
  set_logger,
12
12
  )
13
13
 
14
- __version__ = "7.0.0"
14
+ __version__ = "8.0.0"
15
15
 
16
16
  Unsafe.compute(set_logger(__name__, __version__))
17
17
  LOG = logging.getLogger(__name__)
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from fa_purity import Maybe
8
+
9
+ from fluidattacks_gitlab_sdk.users.core import UserObj
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class IssueGqlFields:
14
+ """Issue fields that only the GraphQL API exposes."""
15
+
16
+ updated_by: Maybe[UserObj]
17
+ status: Maybe[str]
@@ -1,6 +1,6 @@
1
1
  import inspect
2
2
 
3
- from fa_purity import Maybe, ResultE
3
+ from fa_purity import ResultE
4
4
  from fa_purity.json import JsonObj, JsonPrimitiveUnfolder, JsonUnfolder, JsonValue, Unfolder
5
5
  from fluidattacks_etl_utils import smash
6
6
  from fluidattacks_etl_utils.bug import Bug
@@ -25,9 +25,10 @@ from fluidattacks_gitlab_sdk.issues.core import (
25
25
  IssueReferences,
26
26
  IssueType,
27
27
  )
28
- from fluidattacks_gitlab_sdk.users.core import UserObj
29
28
  from fluidattacks_gitlab_sdk.users.decode import decode_user_obj
30
29
 
30
+ from ._core import IssueGqlFields
31
+
31
32
 
32
33
  def decode_type(raw: JsonValue) -> ResultE[IssueType]:
33
34
  return Unfolder.to_primitive(raw).bind(
@@ -67,7 +68,7 @@ def _decode_properties(raw: JsonObj) -> ResultE[IssueOtherProperties]:
67
68
  ).map(lambda t: IssueOtherProperties(*t))
68
69
 
69
70
 
70
- def _decode_refs(updated_by: Maybe[UserObj], raw: JsonObj) -> ResultE[IssueReferences]:
71
+ def _decode_refs(gql_fields: IssueGqlFields, raw: JsonObj) -> ResultE[IssueReferences]:
71
72
  return smash.smash_result_5(
72
73
  JsonUnfolder.require(raw, "author", lambda v: Unfolder.to_json(v).bind(decode_user_obj)),
73
74
  JsonUnfolder.require(
@@ -96,10 +97,10 @@ def _decode_refs(updated_by: Maybe[UserObj], raw: JsonObj) -> ResultE[IssueRefer
96
97
  "assignees",
97
98
  lambda v: Unfolder.to_list_of(v, lambda v: Unfolder.to_json(v).bind(decode_user_obj)),
98
99
  ),
99
- ).map(lambda t: IssueReferences(*t, updated_by))
100
+ ).map(lambda t: IssueReferences(*t, gql_fields.updated_by))
100
101
 
101
102
 
102
- def _decode_def(raw: JsonObj) -> ResultE[IssueDef]:
103
+ def _decode_def(gql_fields: IssueGqlFields, raw: JsonObj) -> ResultE[IssueDef]:
103
104
  return smash.smash_result_4(
104
105
  JsonUnfolder.require(raw, "title", DecodeUtils.to_str),
105
106
  JsonUnfolder.require(raw, "state", DecodeUtils.to_str),
@@ -109,7 +110,7 @@ def _decode_def(raw: JsonObj) -> ResultE[IssueDef]:
109
110
  "description",
110
111
  lambda v: DecodeUtils.to_maybe(v, DecodeUtils.to_str),
111
112
  ),
112
- ).map(lambda t: IssueDef(*t))
113
+ ).map(lambda t: IssueDef(*t, gql_fields.status))
113
114
 
114
115
 
115
116
  def _decode_dates(raw: JsonObj) -> ResultE[IssueDates]:
@@ -133,11 +134,11 @@ def _decode_dates(raw: JsonObj) -> ResultE[IssueDates]:
133
134
  ).map(lambda t: IssueDates(*t))
134
135
 
135
136
 
136
- def decode_issue(updated_by: Maybe[UserObj], raw: JsonObj) -> ResultE[Issue]:
137
+ def decode_issue(gql_fields: IssueGqlFields, raw: JsonObj) -> ResultE[Issue]:
137
138
  return (
138
139
  smash.smash_result_5(
139
- _decode_def(raw),
140
- _decode_refs(updated_by, raw),
140
+ _decode_def(gql_fields, raw),
141
+ _decode_refs(gql_fields, raw),
141
142
  _decode_properties(raw),
142
143
  _decode_dates(raw),
143
144
  _decode_stats(raw),
@@ -155,11 +156,11 @@ def decode_issue(updated_by: Maybe[UserObj], raw: JsonObj) -> ResultE[Issue]:
155
156
 
156
157
 
157
158
  def decode_issue_and_id(
158
- updated_by: Maybe[UserObj],
159
+ gql_fields: IssueGqlFields,
159
160
  raw: JsonObj,
160
161
  ) -> ResultE[tuple[IssueFullId, Issue]]:
161
162
  _id = decode_issue_full_id(raw)
162
163
  return smash.smash_result_2(
163
164
  _id,
164
- decode_issue(updated_by, raw),
165
+ decode_issue(gql_fields, raw),
165
166
  )
@@ -6,7 +6,6 @@ from fa_purity import (
6
6
  CmdTransform,
7
7
  Coproduct,
8
8
  FrozenDict,
9
- Maybe,
10
9
  Result,
11
10
  cast_exception,
12
11
  )
@@ -19,10 +18,10 @@ from fluidattacks_gitlab_sdk._gql_client import GraphQlGitlabClient
19
18
  from fluidattacks_gitlab_sdk._handlers import NotFound, handle_not_found
20
19
  from fluidattacks_gitlab_sdk._http_client import HttpJsonClient, RelativeEndpoint
21
20
  from fluidattacks_gitlab_sdk.ids import IssueFullId, IssueInternalId, ProjectId
22
- from fluidattacks_gitlab_sdk.issues._client._updated_by import get_updated_by
21
+ from fluidattacks_gitlab_sdk.issues._client._gql_fields import get_gql_fields
23
22
  from fluidattacks_gitlab_sdk.issues.core import Issue, ProjectIdObj
24
- from fluidattacks_gitlab_sdk.users.core import UserObj
25
23
 
24
+ from ._core import IssueGqlFields
26
25
  from ._decode import decode_issue_and_id
27
26
 
28
27
  LOG = logging.getLogger(__name__)
@@ -31,7 +30,7 @@ LOG = logging.getLogger(__name__)
31
30
  def _get_issue(
32
31
  client: HttpJsonClient,
33
32
  project: ProjectId,
34
- updated_by: Maybe[UserObj],
33
+ gql_fields: IssueGqlFields,
35
34
  issue_id: IssueInternalId,
36
35
  ) -> Cmd[Result[tuple[IssueFullId, Issue], Coproduct[NotFound, Exception]]]:
37
36
  endpoint = RelativeEndpoint.new(
@@ -56,7 +55,7 @@ def _get_issue(
56
55
  )
57
56
  .bind(lambda i: assert_single(i).alt(Coproduct[NotFound, Exception].inr))
58
57
  .bind(
59
- lambda i: decode_issue_and_id(updated_by, i).alt(
58
+ lambda i: decode_issue_and_id(gql_fields, i).alt(
60
59
  Coproduct[NotFound, Exception].inr,
61
60
  ),
62
61
  )
@@ -83,8 +82,8 @@ def get_issue(
83
82
  project: ProjectIdObj,
84
83
  issue_id: IssueInternalId,
85
84
  ) -> Cmd[Result[tuple[IssueFullId, Issue], Coproduct[NotFound, Exception]]]:
86
- updated_by = get_updated_by(gql_client, project.project_path, issue_id)
85
+ gql_fields = get_gql_fields(gql_client, project.project_path, issue_id)
87
86
  return CmdTransform.chain_cmd_result(
88
- updated_by,
89
- lambda u: _get_issue(client, project.project_id, u, issue_id),
87
+ gql_fields,
88
+ lambda g: _get_issue(client, project.project_id, g, issue_id),
90
89
  ).map(lambda r: r.alt(merge_coproduct))
@@ -15,13 +15,15 @@ from fa_purity.json import JsonObj, JsonUnfolder, JsonValue, Unfolder
15
15
  from fluidattacks_etl_utils.bug import Bug
16
16
  from fluidattacks_etl_utils.decode import DecodeUtils, int_to_str, str_to_int
17
17
  from fluidattacks_etl_utils.natural import Natural
18
- from fluidattacks_etl_utils.smash import right_map
18
+ from fluidattacks_etl_utils.smash import right_map, smash_result_2
19
19
 
20
20
  from fluidattacks_gitlab_sdk._gql_client import GraphQlGitlabClient
21
21
  from fluidattacks_gitlab_sdk._handlers import NotFound
22
22
  from fluidattacks_gitlab_sdk.ids import IssueInternalId, ProjectPath, UserId
23
23
  from fluidattacks_gitlab_sdk.users.core import User, UserName, UserObj
24
24
 
25
+ from ._core import IssueGqlFields
26
+
25
27
  LOG = logging.getLogger(__name__)
26
28
 
27
29
 
@@ -82,6 +84,26 @@ def decode_updated_by(raw: JsonObj) -> ResultE[Maybe[UserObj]]:
82
84
  )
83
85
 
84
86
 
87
+ def decode_status(raw: JsonObj) -> ResultE[Maybe[str]]:
88
+ return JsonUnfolder.require(
89
+ raw,
90
+ "status",
91
+ lambda v: DecodeUtils.to_maybe(
92
+ v,
93
+ lambda v: Unfolder.to_json(v).bind(
94
+ lambda status: JsonUnfolder.require(status, "name", DecodeUtils.to_str),
95
+ ),
96
+ ),
97
+ )
98
+
99
+
100
+ def _decode_gql_fields(raw: JsonObj) -> ResultE[IssueGqlFields]:
101
+ return smash_result_2(
102
+ decode_updated_by(raw),
103
+ decode_status(raw),
104
+ ).map(lambda t: IssueGqlFields(*t))
105
+
106
+
85
107
  def _set_not_found_if_empty[T](
86
108
  value: JsonValue,
87
109
  transform: Callable[[JsonValue], ResultE[T]],
@@ -100,14 +122,14 @@ def _set_not_found_if_empty[T](
100
122
  )
101
123
 
102
124
 
103
- def _decode(raw: JsonObj) -> Result[Maybe[UserObj], Coproduct[NotFound, Exception]]:
125
+ def decode_gql_response(raw: JsonObj) -> Result[IssueGqlFields, Coproduct[NotFound, Exception]]:
104
126
  return (
105
127
  _nested_key(raw, "project", ("issue",))
106
128
  .alt(Coproduct[NotFound, Exception].inr)
107
129
  .bind(
108
130
  lambda v: _set_not_found_if_empty(
109
131
  v,
110
- lambda v: Unfolder.to_json(v).bind(decode_updated_by),
132
+ lambda v: Unfolder.to_json(v).bind(_decode_gql_fields),
111
133
  ),
112
134
  )
113
135
  .alt(
@@ -115,7 +137,7 @@ def _decode(raw: JsonObj) -> Result[Maybe[UserObj], Coproduct[NotFound, Exceptio
115
137
  Coproduct[NotFound, Exception].inl,
116
138
  lambda e: Coproduct[NotFound, Exception].inr(
117
139
  Bug[Exception].new(
118
- "decode_updated_by",
140
+ "decode_gql_fields",
119
141
  inspect.currentframe(),
120
142
  e,
121
143
  (JsonUnfolder.dumps(raw),),
@@ -126,20 +148,20 @@ def _decode(raw: JsonObj) -> Result[Maybe[UserObj], Coproduct[NotFound, Exceptio
126
148
  )
127
149
 
128
150
 
129
- def get_updated_by(
151
+ def get_gql_fields(
130
152
  client: GraphQlGitlabClient,
131
153
  project: ProjectPath,
132
154
  issue_id: IssueInternalId,
133
- ) -> Cmd[Result[Maybe[UserObj], Coproduct[NotFound, Exception]]]:
155
+ ) -> Cmd[Result[IssueGqlFields, Coproduct[NotFound, Exception]]]:
134
156
  """
135
- Get the updatedBy field of an issue.
157
+ Get the updatedBy and status fields of an issue.
136
158
 
137
159
  To avoid failure ensure:
138
160
  - The issue exist
139
161
  - The credentials are correct
140
162
  """
141
163
  query = """
142
- query getIssueUpdatedBy($project: ID!, $issue_iid: String!){
164
+ query getIssueGqlFields($project: ID!, $issue_iid: String!){
143
165
  project(fullPath: $project) {
144
166
  issue(iid: $issue_iid) {
145
167
  updatedBy {
@@ -148,6 +170,9 @@ def get_updated_by(
148
170
  username
149
171
  publicEmail
150
172
  }
173
+ status {
174
+ name
175
+ }
151
176
  }
152
177
  }
153
178
  }
@@ -159,12 +184,12 @@ def get_updated_by(
159
184
  return client.get(query, FrozenTools.freeze(values)).map(
160
185
  lambda r: (
161
186
  r.alt(Coproduct[NotFound, Exception].inr)
162
- .bind(_decode)
187
+ .bind(decode_gql_response)
163
188
  .alt(
164
189
  lambda c: right_map(
165
190
  c,
166
191
  lambda e: Bug[Exception].new(
167
- "get_updated_by",
192
+ "get_gql_fields",
168
193
  inspect.currentframe(),
169
194
  e,
170
195
  (str(project), str(issue_id)),
@@ -27,10 +27,10 @@ from fluidattacks_gitlab_sdk._http_client import (
27
27
  UnhandledErrors,
28
28
  )
29
29
  from fluidattacks_gitlab_sdk.ids import IssueFullId, ProjectPath
30
- from fluidattacks_gitlab_sdk.issues._client._updated_by import get_updated_by
30
+ from fluidattacks_gitlab_sdk.issues._client._gql_fields import get_gql_fields
31
31
  from fluidattacks_gitlab_sdk.issues.core import Issue, ProjectIdObj
32
- from fluidattacks_gitlab_sdk.users.core import UserObj
33
32
 
33
+ from ._core import IssueGqlFields
34
34
  from ._decode import decode_issue_and_id
35
35
 
36
36
  LOG = logging.getLogger(__name__)
@@ -42,18 +42,18 @@ def _most_recent_issue(
42
42
  project: ProjectPath,
43
43
  ) -> Cmd[ResultE[tuple[IssueFullId, Issue]]]:
44
44
  """
45
- Completes the raw issue obj by getting the updated by field.
45
+ Completes the raw issue obj by getting its GraphQL-only fields.
46
46
 
47
47
  This function should be called always after getting the raw issue.
48
48
  It can fail when:
49
49
  - decode fails
50
50
  - raw issue id not exist and/or is fake
51
51
  """
52
- _updated_by: Cmd[ResultE[Maybe[UserObj]]] = (
52
+ _gql_fields: Cmd[ResultE[IssueGqlFields]] = (
53
53
  decode_issue_internal_id(raw)
54
54
  .to_coproduct()
55
55
  .map(
56
- lambda i: get_updated_by(gql_client, project, i.internal_id).map(
56
+ lambda i: get_gql_fields(gql_client, project, i.internal_id).map(
57
57
  lambda r: r.alt(
58
58
  lambda c: c.map(
59
59
  lambda e: Bug[NotFound].new(
@@ -66,14 +66,14 @@ def _most_recent_issue(
66
66
  ),
67
67
  ),
68
68
  ),
69
- lambda e: Cmd[ResultE[Maybe[UserObj]]].wrap_value(
70
- ResultE[Maybe[UserObj]].failure(e),
69
+ lambda e: Cmd[ResultE[IssueGqlFields]].wrap_value(
70
+ ResultE[IssueGqlFields].failure(e),
71
71
  ),
72
72
  )
73
73
  )
74
- return _updated_by.map(
74
+ return _gql_fields.map(
75
75
  lambda r: r.bind(
76
- lambda u: decode_issue_and_id(u, raw),
76
+ lambda g: decode_issue_and_id(g, raw),
77
77
  ),
78
78
  )
79
79
 
@@ -73,6 +73,7 @@ class IssueDef:
73
73
  state: str
74
74
  issue_type: IssueType
75
75
  description: Maybe[str]
76
+ status: Maybe[str]
76
77
 
77
78
 
78
79
  @dataclass(frozen=True)
@@ -50,8 +50,10 @@ _dag: dict[str, tuple[tuple[str, ...] | str, ...]] = {
50
50
  "core",
51
51
  ),
52
52
  "fluidattacks_gitlab_sdk.issues._client": (
53
- ("_get_issue", "_most_recent", "_updated_by"),
53
+ ("_get_issue", "_most_recent"),
54
+ "_gql_fields",
54
55
  "_decode",
56
+ "_core",
55
57
  ),
56
58
  "fluidattacks_gitlab_sdk.members": (
57
59
  "_client",
@@ -20,6 +20,7 @@ from fluidattacks_gitlab_sdk.ids import (
20
20
  ProjectId,
21
21
  UserId,
22
22
  )
23
+ from fluidattacks_gitlab_sdk.issues._client._core import IssueGqlFields
23
24
  from fluidattacks_gitlab_sdk.issues._client._decode import decode_issue_and_id
24
25
  from fluidattacks_gitlab_sdk.issues.core import (
25
26
  Issue,
@@ -47,7 +48,13 @@ def test_decode() -> None:
47
48
  Maybe[str].some(""),
48
49
  )
49
50
  closed_by = assignee
50
- issue_def = IssueDef("The title", "closed", IssueType.ISSUE, Maybe[str].some(""))
51
+ issue_def = IssueDef(
52
+ "The title",
53
+ "closed",
54
+ IssueType.ISSUE,
55
+ Maybe[str].some(""),
56
+ Maybe[str].some("Won't do"),
57
+ )
51
58
  milestone_id = MilestoneFullId(
52
59
  MilestoneGlobalId(NaturalOperations.absolute(5)),
53
60
  MilestoneFullInternalId(
@@ -108,7 +115,10 @@ def test_decode() -> None:
108
115
  .to_union()
109
116
  )
110
117
  decoded_issue_obj = (
111
- decode_issue_and_id(Maybe[UserObj].some(author), raw_data)
118
+ decode_issue_and_id(
119
+ IssueGqlFields(Maybe[UserObj].some(author), Maybe[str].some("Won't do")),
120
+ raw_data,
121
+ )
112
122
  .alt(Unsafe.raise_exception)
113
123
  .to_union()
114
124
  )
@@ -0,0 +1,70 @@
1
+ from fa_purity import Maybe, ResultE, Unsafe, cast_exception
2
+ from fa_purity.json import JsonObj, UnfoldedFactory
3
+ from fluidattacks_etl_utils.natural import NaturalOperations
4
+
5
+ from fluidattacks_gitlab_sdk.ids import UserId
6
+ from fluidattacks_gitlab_sdk.issues._client._core import IssueGqlFields
7
+ from fluidattacks_gitlab_sdk.issues._client._gql_fields import (
8
+ decode_gql_response,
9
+ decode_status,
10
+ )
11
+ from fluidattacks_gitlab_sdk.users.core import User, UserName, UserObj
12
+
13
+
14
+ def _json(raw: str) -> JsonObj:
15
+ return UnfoldedFactory.loads(raw).alt(Unsafe.raise_exception).to_union()
16
+
17
+
18
+ def _is_failure[T](result: ResultE[T]) -> bool:
19
+ return result.map(lambda _: False).value_or(True)
20
+
21
+
22
+ def test_status_with_name() -> None:
23
+ result = decode_status(_json('{"status": {"name": "Won\'t do"}}'))
24
+ assert result.alt(Unsafe.raise_exception).to_union() == Maybe[str].some("Won't do")
25
+
26
+
27
+ def test_null_status() -> None:
28
+ result = decode_status(_json('{"status": null}'))
29
+ assert result.alt(Unsafe.raise_exception).to_union() == Maybe[str].empty()
30
+
31
+
32
+ def test_missing_status_key_fails() -> None:
33
+ assert _is_failure(decode_status(_json("{}")))
34
+
35
+
36
+ def test_status_without_name_fails() -> None:
37
+ assert _is_failure(decode_status(_json('{"status": {}}')))
38
+
39
+
40
+ def test_non_string_name_fails() -> None:
41
+ assert _is_failure(decode_status(_json('{"status": {"name": 7}}')))
42
+
43
+
44
+ def test_decode_gql_response() -> None:
45
+ raw = _json(
46
+ """
47
+ {
48
+ "project": {
49
+ "issue": {
50
+ "updatedBy": {
51
+ "id": "gid://gitlab/User/1",
52
+ "name": "Ghost User",
53
+ "username": "ghost1",
54
+ "publicEmail": null
55
+ },
56
+ "status": {"name": "Duplicate"}
57
+ }
58
+ }
59
+ }
60
+ """,
61
+ )
62
+ author = UserObj(
63
+ UserId(NaturalOperations.absolute(1)),
64
+ User("ghost1"),
65
+ UserName("Ghost User"),
66
+ Maybe[str].empty(),
67
+ )
68
+ expected = IssueGqlFields(Maybe[UserObj].some(author), Maybe[str].some("Duplicate"))
69
+ result = decode_gql_response(raw).alt(lambda c: c.map(cast_exception, cast_exception))
70
+ assert result.alt(Unsafe.raise_exception).to_union() == expected
@@ -320,14 +320,14 @@ wheels = [
320
320
 
321
321
  [[package]]
322
322
  name = "gitpython"
323
- version = "3.1.59"
323
+ version = "3.1.62"
324
324
  source = { registry = "https://pypi.org/simple" }
325
325
  dependencies = [
326
326
  { name = "gitdb" },
327
327
  ]
328
- sdist = { url = "https://files.pythonhosted.org/packages/ca/dc/126b28e76b24a9268ba931ad3e012f71ebdadf62fd9f17758f7074bb0b20/gitpython-3.1.59.tar.gz", hash = "sha256:0a1475cfdc38a5bfba1a3e9a4a9da52a39749ecec322b772915c019f94e5b7e4", size = 230445, upload-time = "2026-08-10T12:03:20.271Z" }
328
+ sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" }
329
329
  wheels = [
330
- { url = "https://files.pythonhosted.org/packages/ef/ed/ae57eb7d344f43f87b74b3a281ead6ec7d6394eef72a7b1dcb28dd089550/gitpython-3.1.59-py3-none-any.whl", hash = "sha256:67a82f537384578643624c8b2c531938a9b82be431663e575dcf638526631d4c", size = 220996, upload-time = "2026-08-10T12:03:18.804Z" },
330
+ { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" },
331
331
  ]
332
332
 
333
333
  [[package]]
@@ -426,47 +426,34 @@ wheels = [
426
426
 
427
427
  [[package]]
428
428
  name = "multidict"
429
- version = "6.7.1"
430
- source = { registry = "https://pypi.org/simple" }
431
- sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" }
432
- wheels = [
433
- { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" },
434
- { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" },
435
- { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" },
436
- { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" },
437
- { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" },
438
- { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" },
439
- { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" },
440
- { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" },
441
- { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" },
442
- { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" },
443
- { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" },
444
- { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" },
445
- { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" },
446
- { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" },
447
- { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" },
448
- { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" },
449
- { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" },
450
- { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" },
451
- { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" },
452
- { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" },
453
- { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" },
454
- { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" },
455
- { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" },
456
- { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" },
457
- { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" },
458
- { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" },
459
- { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" },
460
- { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" },
461
- { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" },
462
- { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" },
463
- { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" },
464
- { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" },
465
- { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" },
466
- { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" },
467
- { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" },
468
- { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" },
469
- { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" },
429
+ version = "7.0.0"
430
+ source = { registry = "https://pypi.org/simple" }
431
+ sdist = { url = "https://files.pythonhosted.org/packages/c4/64/642465a4827331a98ba4ae29f97658be25d1bdcb868872a2f78f7482b507/multidict-7.0.0.tar.gz", hash = "sha256:a7fcd089a0af2e0ef053c0d39c22c9ebf2434dddcb91034fd2f59ec99623788e", size = 280561, upload-time = "2026-09-26T19:36:12.283Z" }
432
+ wheels = [
433
+ { url = "https://files.pythonhosted.org/packages/5c/6e/c388fccb88dd6cb41ce2454e420c869fb0629ce10bfe12e4cad578ba940d/multidict-7.0.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:15d9ad70615ce1b37c124445145f071ba4eaa88cb21b49a6b81727cc85aefc37", size = 92310, upload-time = "2026-09-26T19:32:24.366Z" },
434
+ { url = "https://files.pythonhosted.org/packages/58/2f/33505a5c498613d53ab1d87b3c9425f6757afc7e48c2409f7aa001d0cdc1/multidict-7.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:313e336c75a53447cd943d93235c4702428b9740cb681e7174e398ba1f3f2918", size = 85114, upload-time = "2026-09-26T19:32:25.975Z" },
435
+ { url = "https://files.pythonhosted.org/packages/76/f5/27b19597821cb80d082b649e5e6e01033aed5e0eebdbbc6a644eb49515a7/multidict-7.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a4838959217e2652ef7dce1d01bb370aed3e5b694eb7a8258fa1cc50beef08ca", size = 86952, upload-time = "2026-09-26T19:32:27.429Z" },
436
+ { url = "https://files.pythonhosted.org/packages/f7/58/a5e44d5453c8bb295979d242cfd3beee06f6ab9f6c1c06a13c2191b792b1/multidict-7.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bd6520538e19fe104c3b74cc64767d6f8959dd2c9b8d59da62d83641ea646deb", size = 147356, upload-time = "2026-09-26T19:32:28.816Z" },
437
+ { url = "https://files.pythonhosted.org/packages/a2/c0/4da412a7267ac85759300a078f3bdad986e18a30c1ead8b74d2e3b335a2b/multidict-7.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e9bf055becb08ff797d5cf80b760b726b8babb39046564d2b8481bbe6504bce", size = 88022, upload-time = "2026-09-26T19:32:30.44Z" },
438
+ { url = "https://files.pythonhosted.org/packages/b6/46/bebeb7df10298d390942e61bc7fc48472183ee4f5a0b7c285d93369b6874/multidict-7.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8e443cab381f8b6d24950982ae508600d464412cbdcf78e263cf8cd9b8372d68", size = 87046, upload-time = "2026-09-26T19:32:31.881Z" },
439
+ { url = "https://files.pythonhosted.org/packages/ab/d3/38b254c21cb8e9a65f8751ad8ba0a6f9baad75e3b6d3e559d1d05db26482/multidict-7.0.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e45bb7b9c850f216c0081dab8c6862fdec6b8be1c800b2a1aa4d1e56dcfacb54", size = 488729, upload-time = "2026-09-26T19:32:33.338Z" },
440
+ { url = "https://files.pythonhosted.org/packages/2a/28/19d66c476cc44fffdeacd5227a59046f94612075008069286bdbb9d3fa35/multidict-7.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2f742000b47d806e5798f5ef0cdb2ed43d822fc913331a33ed1a40515495a7", size = 496708, upload-time = "2026-09-26T19:32:35.286Z" },
441
+ { url = "https://files.pythonhosted.org/packages/0d/66/3bb2d2cdf67742b002985906785b0db1584ca2e67ddb3677f6e9614321d6/multidict-7.0.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70e28954ae5ae3c1806aa6d85bbb26ef7ab73017228eb236a3f245e24720bb34", size = 458261, upload-time = "2026-09-26T19:32:37.027Z" },
442
+ { url = "https://files.pythonhosted.org/packages/af/5c/c8ca15c191f957e8d86ec65148ed1e8ca189f456e66153e23c4ae610649a/multidict-7.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c41ccfa34b152146ccf05e8da753a85f8e67437477f8a00783aa427a132abd31", size = 511932, upload-time = "2026-09-26T19:32:39.025Z" },
443
+ { url = "https://files.pythonhosted.org/packages/14/ff/0665b2c3d5e42165a96fffc4c34c3d5fa66d0819d82b9bc2977b9a6aa1ed/multidict-7.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9a1de5f1da3ae056b45b6599a53dba6c86879bdfa5c3f15d7dde99f6cfbc03c6", size = 529152, upload-time = "2026-09-26T19:32:41.068Z" },
444
+ { url = "https://files.pythonhosted.org/packages/ab/f7/967875e5d1f908613cfcd5d9d078011168acb88a128028b81a0de43e8aa6/multidict-7.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:094852ebc620a10ee0e11f36a68e016d73596c0f5ed8351a9a52fcff7d449ad6", size = 506337, upload-time = "2026-09-26T19:32:42.839Z" },
445
+ { url = "https://files.pythonhosted.org/packages/a8/f9/56f04cf560bf736c23ba9cacc1ae3fcd2bbb591ba4f166c4bc08f5fead61/multidict-7.0.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03cc4027103bb61feac20336a15fced1a6a369fed6a61c516353921207e4578c", size = 448144, upload-time = "2026-09-26T19:32:44.556Z" },
446
+ { url = "https://files.pythonhosted.org/packages/07/ea/ff7978bb38f2e2908cf7fc80061175cbfacdcada248dc48fb79a10c0e92b/multidict-7.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0fef8622896443fd0bdf39586391c2162ac05004ba4c9918827dfd2f79da750c", size = 485535, upload-time = "2026-09-26T19:32:46.439Z" },
447
+ { url = "https://files.pythonhosted.org/packages/ed/6c/1e9a6457587da0a31b3ef31ca0a40624877ca29408c8a7385260a3d45cd9/multidict-7.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1847d33ca198aba3bf5fad036a7e0376cca325e7ec89f26362f9aa48994d8146", size = 471078, upload-time = "2026-09-26T19:32:48.166Z" },
448
+ { url = "https://files.pythonhosted.org/packages/ea/12/ee11505f0ae23a344544010485db982290c0e6105caefd74ce3830c315ca/multidict-7.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b5c93f582f8a973ea803c46097729e34969683bc99f99fc00321b1f76317e81", size = 490764, upload-time = "2026-09-26T19:32:49.861Z" },
449
+ { url = "https://files.pythonhosted.org/packages/b9/8d/1e954f7cbb1f2080c2712b6a4cb6ce196037c06c686ae030003465b65ad7/multidict-7.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7c717376f801dd1ee102b292e0a4ca6037e948a8710316e64e698fe39eeb1307", size = 500752, upload-time = "2026-09-26T19:32:51.756Z" },
450
+ { url = "https://files.pythonhosted.org/packages/da/54/0f3899f51364f502219192b8c03da123c8618e9709499194ecd8e4143cc9/multidict-7.0.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c6d6e73b9f7b70558de5624b036815464a7ead4671fe2c3a4b3ce444c240cba6", size = 444706, upload-time = "2026-09-26T19:32:53.515Z" },
451
+ { url = "https://files.pythonhosted.org/packages/54/03/8edc3cb05a35185a4910582a50aa4dd342cceda3b26b2053dbd004adc9e8/multidict-7.0.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:18f627c69d041ae17d8fb45dbc63e192a2afc3ba062195441b5a50e93c50ae68", size = 511149, upload-time = "2026-09-26T19:32:55.281Z" },
452
+ { url = "https://files.pythonhosted.org/packages/d1/b4/4234b76f97b2966d1de2f3507f5dc9af4072e2ed16cfb476855490e34ae9/multidict-7.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8477e14353097077e1011464794d11e1d7559e821ab7c612cf7c06e7826ee1ba", size = 501451, upload-time = "2026-09-26T19:32:57.073Z" },
453
+ { url = "https://files.pythonhosted.org/packages/d8/a9/fe7965b74cb3cf822d3e88063f13e3eff2600c36022f69c603b5b52f3e21/multidict-7.0.0-cp313-cp313-win32.whl", hash = "sha256:2efca6de4fbefc12f2aaac2d5e1c176a3864d22250246b44fe3823e3634f06f8", size = 76414, upload-time = "2026-09-26T19:32:58.84Z" },
454
+ { url = "https://files.pythonhosted.org/packages/e9/c7/a4960e81ba8a3a9698011d1ac92d4d11ce8c9bb0ed6d235de76c49cae3d1/multidict-7.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:188a7f89a1935a138237088bff22400bacd25d80fb0d3a8a26fab25961df2239", size = 87005, upload-time = "2026-09-26T19:33:00.576Z" },
455
+ { url = "https://files.pythonhosted.org/packages/6c/66/7f6b0898c895f2b39ebaa3fc34d32537d263aa5c74809ee747b5e705da80/multidict-7.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:debb701a0436c96f2717c6156428a2c8bce2559236e120a674d503aeed678aa3", size = 85466, upload-time = "2026-09-26T19:33:02.072Z" },
456
+ { url = "https://files.pythonhosted.org/packages/46/a4/a3e9e72ed0f99587e9e96c3de72b743e1385e5e2b15fb583d8dfc05ffffb/multidict-7.0.0-py3-none-any.whl", hash = "sha256:1ba6a45450243bd8953223adff7183fce5e3f9f3556a03bd17a674caaa046215", size = 26592, upload-time = "2026-09-26T19:36:09.764Z" },
470
457
  ]
471
458
 
472
459
  [[package]]
@@ -905,11 +892,11 @@ wheels = [
905
892
 
906
893
  [[package]]
907
894
  name = "urllib3"
908
- version = "2.7.0"
895
+ version = "2.8.0"
909
896
  source = { registry = "https://pypi.org/simple" }
910
- sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
897
+ sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
911
898
  wheels = [
912
- { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
899
+ { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
913
900
  ]
914
901
 
915
902
  [[package]]