PyLinks 0.0.0.dev47__tar.gz → 0.0.0.dev48__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 (36) hide show
  1. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/PKG-INFO +3 -3
  2. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/pyproject.toml +3 -3
  3. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/PyLinks.egg-info/PKG-INFO +3 -3
  4. pylinks-0.0.0.dev48/src/PyLinks.egg-info/requires.txt +3 -0
  5. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/api/github.py +132 -36
  6. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/api/zenodo.py +107 -22
  7. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/exception/api.py +10 -4
  8. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/http.py +2 -2
  9. pylinks-0.0.0.dev47/src/PyLinks.egg-info/requires.txt +0 -3
  10. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/README.md +0 -0
  11. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/setup.cfg +0 -0
  12. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/PyLinks.egg-info/SOURCES.txt +0 -0
  13. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/PyLinks.egg-info/dependency_links.txt +0 -0
  14. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/PyLinks.egg-info/not-zip-safe +0 -0
  15. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/PyLinks.egg-info/top_level.txt +0 -0
  16. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/__init__.py +0 -0
  17. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/_settings.py +0 -0
  18. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/api/__init__.py +0 -0
  19. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/api/doi.py +0 -0
  20. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/api/orcid.py +0 -0
  21. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/exception/__init__.py +0 -0
  22. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/exception/base.py +0 -0
  23. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/exception/media_type.py +0 -0
  24. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/exception/uri.py +0 -0
  25. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/media_type.py +0 -0
  26. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/__init__.py +0 -0
  27. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/binder.py +0 -0
  28. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/conda.py +0 -0
  29. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/github.py +0 -0
  30. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/lib_io.py +0 -0
  31. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/pypi.py +0 -0
  32. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/site/readthedocs.py +0 -0
  33. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/string.py +0 -0
  34. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/uri/__init__.py +0 -0
  35. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/uri/data.py +0 -0
  36. {pylinks-0.0.0.dev47 → pylinks-0.0.0.dev48}/src/pylinks/url.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyLinks
3
- Version: 0.0.0.dev47
3
+ Version: 0.0.0.dev48
4
4
  Requires-Python: >=3.10
5
5
  Requires-Dist: requests<3,>=2.31.0
6
- Requires-Dist: ExceptionMan==0.0.0.dev34
7
- Requires-Dist: MDit==0.0.0.dev34
6
+ Requires-Dist: ExceptionMan==0.0.0.dev35
7
+ Requires-Dist: MDit==0.0.0.dev35
@@ -17,12 +17,12 @@ namespaces = true
17
17
  # ----------------------------------------- Project Metadata -------------------------------------
18
18
  #
19
19
  [project]
20
- version = "0.0.0.dev47"
20
+ version = "0.0.0.dev48"
21
21
  name = "PyLinks"
22
22
  dependencies = [
23
23
  "requests >= 2.31.0, < 3",
24
- "ExceptionMan == 0.0.0.dev34",
25
- "MDit == 0.0.0.dev34",
24
+ "ExceptionMan == 0.0.0.dev35",
25
+ "MDit == 0.0.0.dev35",
26
26
  ]
27
27
  requires-python = ">=3.10"
28
28
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyLinks
3
- Version: 0.0.0.dev47
3
+ Version: 0.0.0.dev48
4
4
  Requires-Python: >=3.10
5
5
  Requires-Dist: requests<3,>=2.31.0
6
- Requires-Dist: ExceptionMan==0.0.0.dev34
7
- Requires-Dist: MDit==0.0.0.dev34
6
+ Requires-Dist: ExceptionMan==0.0.0.dev35
7
+ Requires-Dist: MDit==0.0.0.dev35
@@ -0,0 +1,3 @@
1
+ requests<3,>=2.31.0
2
+ ExceptionMan==0.0.0.dev35
3
+ MDit==0.0.0.dev35
@@ -1,5 +1,6 @@
1
1
  # Standard libraries
2
- from typing import Optional, Literal
2
+ from __future__ import annotations as _annotations
3
+ from typing import TYPE_CHECKING as _TYPE_CHECKING
3
4
  from pathlib import Path
4
5
  import re
5
6
  import mimetypes
@@ -7,6 +8,9 @@ import mimetypes
7
8
  # Non-standard libraries
8
9
  import pylinks as _pylinks
9
10
 
11
+ if _TYPE_CHECKING:
12
+ from typing import Optional, Literal, Any
13
+
10
14
 
11
15
  class GitHub:
12
16
  """GitHub API
@@ -38,13 +42,22 @@ class GitHub:
38
42
  def graphql_query(
39
43
  self,
40
44
  query: str,
45
+ variables: dict[str, tuple[Any, str, bool]] | None = None,
41
46
  extra_headers: dict | None = None,
42
47
  ) -> dict:
43
48
  headers = self._headers | extra_headers if extra_headers else self._headers
49
+ if variables:
50
+ args = ", ".join(
51
+ f"${name}:{typ}{"!" if required else ""}" for name, (_, typ, required) in variables.items()
52
+ )
53
+ sig = f"query({args})"
54
+ else:
55
+ sig = "query"
44
56
  response = _pylinks.http.graphql_query(
45
57
  url=self._endpoint["api"] / "graphql",
46
- query=f"{{{query}}}",
58
+ query=f"{sig} {{{query}}}",
47
59
  headers=headers,
60
+ variables={name: value for name, (value, _, _) in variables.items()} if variables else None
48
61
  )
49
62
  return response
50
63
 
@@ -169,10 +182,12 @@ class Repo:
169
182
  def _graphql_query(
170
183
  self,
171
184
  payload: str,
185
+ variables: dict[str, tuple[Any, str, bool]] | None = None,
172
186
  extra_headers: dict | None = None,
173
187
  ) -> dict:
174
188
  return self._github.graphql_query(
175
189
  query=f'repository(name: "{self._name}", owner: "{self._username}") {{{payload}}}',
190
+ variables=variables,
176
191
  extra_headers=extra_headers,
177
192
  )["repository"]
178
193
 
@@ -620,6 +635,8 @@ class Repo:
620
635
  self,
621
636
  number: int,
622
637
  count: int = 0,
638
+ cursor_before: str | None = None,
639
+ cursor_after: str | None = None,
623
640
  sort: Literal["first", "last"] = "last",
624
641
  ) -> list[dict]:
625
642
  """
@@ -640,20 +657,52 @@ class Repo:
640
657
  ----------
641
658
  - [GitHub API Docs](https://docs.github.com/en/rest/pulls/commits?apiVersion=2022-11-28#list-commits-on-a-pull-request)
642
659
  """
643
- payload = f"pullRequest(number: {number})"
644
- commits = "{{commits()"
645
-
646
- commits = []
647
- page = 1
660
+ def post_process():
661
+ out = []
662
+ for datum in data:
663
+ commits = datum["pullRequest"]["commits"]["nodes"]
664
+ if sort == "last":
665
+ commits = reversed(commits)
666
+ for commit in commits:
667
+ commit["commit"]["authors"] = commit["commit"]["authors"]["nodes"]
668
+ out.append(commit)
669
+ return out
670
+
671
+ git_actor_fields = "{name, email, date user {id, login}}"
672
+ commit_fields = f"{{abbreviatedOid, additions, deletions, authors(first: 100) {{nodes {git_actor_fields}}}, committer {git_actor_fields}, authoredByCommitter, authoredDate, committedDate, message, messageBody, messageHeadline, oid, id, resourcePath, url}}"
673
+ page_info_fields = "pageInfo {startCursor, endCursor, hasNextPage, hasPreviousPage}"
674
+ commits_args = ["after: $after", "before: $before", f"{sort}: {100 if count <= 0 else min(count, 100)}"]
675
+ commits_fields = f"nodes {{id, resourcePath, url, commit {commit_fields} }}"
676
+ commits_sig = f"commits({", ".join(commits_args)})"
677
+ payload = f"pullRequest(number: {number}) {{ {commits_sig} {{ {commits_fields} {page_info_fields} }} }}"
678
+ variables = {
679
+ "after": (cursor_after, "String", False),
680
+ "before": (cursor_before, "String", False),
681
+ }
682
+ data = [self._graphql_query(payload, variables)]
683
+ total_downloaded = 100
648
684
  while True:
649
- response = self._rest_query(f"pulls/{number}/commits?per_page=100&page={page}")
650
- commits.extend(response)
651
- page += 1
652
- if len(response) < 100:
653
- break
654
- return commits
655
-
656
-
685
+ page_info = data[-1]["pullRequest"]["commits"]["pageInfo"]
686
+ if count <= 0:
687
+ must_continue = page_info["hasNextPage" if sort == "first" else "hasPreviousPage"]
688
+ if not must_continue:
689
+ return post_process()
690
+ elif total_downloaded >= count:
691
+ return post_process()
692
+ else:
693
+ variables[sort] = page_info["endCursor" if sort == "first" else "startCursor"]
694
+ data.append(self._graphql_query(payload, variables))
695
+ total_downloaded += 100
696
+
697
+ # commits = []
698
+ # page = 1
699
+ # while True:
700
+ # response = self._rest_query(f"pulls/{number}/commits?per_page=100&page={page}")
701
+ # commits.extend(response)
702
+ # page += 1
703
+ # if len(response) < 100:
704
+ # break
705
+ # return commits
657
706
 
658
707
  def pull_create(
659
708
  self,
@@ -1046,15 +1095,22 @@ class Repo:
1046
1095
  raise ValueError("At least one of 'new_name', 'color', or 'description' must be specified.")
1047
1096
  return self._rest_query(query=f"labels/{name}", verb="PATCH", json=data)
1048
1097
 
1098
+ def release_get(self, release_id: int) -> dict:
1099
+ return self._rest_query(query=f"releases/{release_id}")
1100
+
1101
+ def release_delete(self, release_id: int) -> None:
1102
+ self._rest_query(query=f"releases/{release_id}", verb="DELETE", response_type=None)
1103
+ return
1104
+
1049
1105
  def release_create(
1050
1106
  self,
1051
1107
  tag_name: str,
1052
- name: str,
1053
- body: str,
1054
- target_commitish: str = "",
1108
+ name: str | None = None,
1109
+ body: str | None = None,
1110
+ target_commitish: str | None = None,
1055
1111
  draft: bool = False,
1056
1112
  prerelease: bool = False,
1057
- discussion_category_name: str = "",
1113
+ discussion_category_name: str | None = None,
1058
1114
  generate_release_notes: bool = False,
1059
1115
  make_latest: Literal['true', 'false', 'legacy'] = 'true'
1060
1116
  ):
@@ -1094,22 +1150,62 @@ class Repo:
1094
1150
  ----------
1095
1151
  [GitHub API Docs](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release)
1096
1152
  """
1097
- data = {
1098
- "tag_name": tag_name,
1099
- "draft": draft,
1100
- "prerelease": prerelease,
1101
- "generate_release_notes": generate_release_notes,
1102
- "make_latest": make_latest,
1103
- }
1104
- if name:
1105
- data["name"] = name
1106
- if body:
1107
- data["body"] = body
1108
- if target_commitish:
1109
- data["target_commitish"] = target_commitish
1110
- if discussion_category_name:
1111
- data["discussion_category_name"] = discussion_category_name
1112
- return self._rest_query(query="releases", verb="POST", json=data)
1153
+ data = {k: v for k, v in locals().items() if k != "self" and v is not None}
1154
+ return self._rest_query(query=f"releases", verb="POST", json=data)
1155
+
1156
+ def release_update(
1157
+ self,
1158
+ release_id: int,
1159
+ tag_name: str | None = None,
1160
+ name: str | None = None,
1161
+ body: str | None = None,
1162
+ target_commitish: str | None = None,
1163
+ draft: bool | None = None,
1164
+ prerelease: bool | None = None,
1165
+ discussion_category_name: str | None = None,
1166
+ make_latest: Literal['true', 'false', 'legacy'] | None = None
1167
+ ):
1168
+ """Update a release.
1169
+
1170
+ Parameters
1171
+ ----------
1172
+ tag_name : str
1173
+ The name of the tag.
1174
+ name : str
1175
+ The name of the release.
1176
+ body : str
1177
+ The body of the release post, i.e. text describing the release.
1178
+ target_commitish : str, optional
1179
+ The commitish value that determines where the Git tag is created from.
1180
+ Can be any branch or commit SHA. Unused if the Git tag already exists.
1181
+ The default is the repository's default branch.
1182
+ draft : bool, optional
1183
+ `True` to create a draft (unpublished) release, `False` to create a published one.
1184
+ prerelease : bool, default: False
1185
+ `True` to identify the release as a prerelease, `False` to identify it as a full release.
1186
+ discussion_category_name : str, optional
1187
+ The name of the discussion category for the release, to be created and linked to the release.
1188
+ The value must be a category that already exists in the repository.
1189
+ make_latest : {'true', 'false', 'legacy'}, optional
1190
+ Whether this release should be set as the latest release for the repository.
1191
+ Drafts and prereleases cannot be set as latest.
1192
+ Defaults to 'true' for newly published releases.
1193
+ 'legacy' specifies that the latest release should be determined based on
1194
+ the release creation date and higher semantic version.
1195
+
1196
+ References
1197
+ ----------
1198
+ [GitHub API Docs](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release)
1199
+ """
1200
+ data = {k: v for k, v in locals().items() if k not in ("self", "release_id") and v is not None}
1201
+ return self._rest_query(query=f"releases/{release_id}", verb="PATCH", json=data)
1202
+
1203
+ def release_asset_list(self, release_id: int) -> list[dict]:
1204
+ return self._rest_query(query=f"releases/{release_id}/assets")
1205
+
1206
+ def release_asset_delete(self, asset_id: int) -> None:
1207
+ self._rest_query(query=f"releases/assets/{asset_id}", verb="DELETE", response_type=None)
1208
+ return
1113
1209
 
1114
1210
  def release_asset_upload(
1115
1211
  self,
@@ -1701,7 +1797,7 @@ class Repo:
1701
1797
  """
1702
1798
  payload = "branchProtectionRules(first: 100) {nodes {id, pattern}}"
1703
1799
  data = self._graphql_query(payload)
1704
- return data["repository"]["branchProtectionRules"]["nodes"]
1800
+ return data["branchProtectionRules"]["nodes"]
1705
1801
 
1706
1802
  def branch_protection_rule_create(
1707
1803
  self,
@@ -18,37 +18,90 @@ class Zenodo:
18
18
  """
19
19
  def __init__(self, token: str, sandbox: bool = False):
20
20
  self._sandbox = sandbox
21
- self._url = _pylinks.url.create("https://zenodo.org/api")
22
- self._url_sandbox = _pylinks.url.create("https://sandbox.zenodo.org/api")
23
- self._headers = {"Authorization": token}
21
+ self._url = _pylinks.url.create(
22
+ "https://sandbox.zenodo.org/api" if sandbox else "https://zenodo.org/api"
23
+ )
24
+ self._headers = {"Authorization": f"Bearer {token}"}
24
25
  return
25
26
 
26
27
  def rest_query(
27
28
  self,
28
29
  query: str,
29
30
  verb: Literal["GET", "POST", "PUT", "PATCH", "OPTIONS", "DELETE"] = "GET",
31
+ params: dict | None = None,
30
32
  data = None,
31
33
  json = None,
32
34
  content_type: str | None = "application/json",
33
- sandbox: bool | None = None,
35
+ response_type: Literal["str", "json", "bytes"] | None = "json"
34
36
  ) -> dict | list:
35
- sandbox = sandbox if sandbox is not None else self._sandbox
36
- base_url = self._url_sandbox if sandbox else self._url
37
37
  content_header = {"Content-Type": content_type} if content_type else {}
38
38
  return _pylinks.http.request(
39
- url=base_url / query,
39
+ url=self._url / query,
40
40
  verb=verb,
41
+ params=params,
41
42
  data=data,
42
43
  json=json,
43
44
  headers=self._headers | content_header,
44
- response_type="json", # All responses are JSON (https://developers.zenodo.org/#responses)
45
+ response_type=response_type, # All responses are JSON (https://developers.zenodo.org/#responses)
45
46
  )
46
47
 
47
- def deposition_create(
48
+ def create_and_publish(
48
49
  self,
49
- metadata: dict | None = None,
50
- sandbox: bool | None = None,
51
- ) -> dict:
50
+ metadata: dict,
51
+ files: list[str | _Path | tuple[str | _Path, str]],
52
+ previous_id: str | int | None = None
53
+ ):
54
+ def add_files(bucket_id: str):
55
+ for file in files:
56
+ if not isinstance(file, (str, _Path)):
57
+ filepath = file[0]
58
+ name = file[1]
59
+ else:
60
+ filepath = file
61
+ name = None
62
+ self.file_create(
63
+ bucket_id=bucket_id,
64
+ filepath=filepath,
65
+ name=name,
66
+ )
67
+ return
68
+
69
+ if not previous_id:
70
+ new_depo = self.deposition_create(metadata=metadata)
71
+ add_files(bucket_id=new_depo["links"]["bucket"])
72
+ return self.deposition_publish(new_depo["id"])
73
+ new_ver = self.deposition_new_version(deposition_id=previous_id)
74
+ for previous_file in new_ver["files"]:
75
+ self.file_delete(deposition_id=new_ver["id"], file_id=previous_file["id"])
76
+ add_files(new_ver["links"]["bucket"])
77
+ return self.deposition_publish(new_ver["id"])
78
+
79
+
80
+ def deposition_list(
81
+ self,
82
+ query: str | None = None,
83
+ status: Literal["draft", "published"] | None = None,
84
+ sort: Literal["bestmatch", "mostrecent", "-bestmatch", "-mostrecent"] | None = None,
85
+ page: int | None = None,
86
+ size: int | None = None,
87
+ all_versions: bool | None = None,
88
+ ):
89
+ params = {k: v for k, v in locals().items() if k not in ("self", "query") and v}
90
+ if query:
91
+ params["q"] = query
92
+ return self.rest_query(
93
+ "deposit/depositions",
94
+ verb="GET",
95
+ params=params,
96
+ )
97
+
98
+ def deposition_retrieve(self, deposition_id: str | int):
99
+ return self.rest_query(
100
+ f"deposit/depositions/{deposition_id}",
101
+ verb="GET",
102
+ )
103
+
104
+ def deposition_create(self, metadata: dict | None = None) -> dict:
52
105
  """Create a new deposition.
53
106
 
54
107
  Returns
@@ -91,25 +144,51 @@ class Zenodo:
91
144
  return self.rest_query(
92
145
  query="deposit/depositions",
93
146
  verb="POST",
94
- json=metadata or {},
95
- sandbox=sandbox,
147
+ json={"metadata": metadata} if metadata else {},
96
148
  )
97
149
 
98
- def deposition_publish(self, deposition_id: str) -> dict:
150
+ def deposition_new_version(self, deposition_id: int | str):
151
+ """Create a new version of a deposition as a draft."""
152
+ return self.rest_query(
153
+ query=f"deposit/depositions/{deposition_id}/actions/newversion",
154
+ verb="POST",
155
+ )
156
+
157
+ def deposition_update(self, deposition_id: int | str, metadata: dict):
158
+ """Update and existing deposition."""
159
+ return self.rest_query(
160
+ query=f"deposit/depositions/{deposition_id}",
161
+ verb="PUT",
162
+ json={"metadata": metadata},
163
+ )
164
+
165
+ def deposition_publish(self, deposition_id: int | str) -> dict:
99
166
  """Publish a deposition."""
100
167
  return self.rest_query(
101
168
  query=f"deposit/depositions/{deposition_id}/actions/publish",
102
169
  verb="POST",
103
170
  )
104
171
 
172
+ def file_list(self, deposition_id: str | int):
173
+ return self.rest_query(
174
+ f"deposit/depositions/{deposition_id}/files",
175
+ verb="GET"
176
+ )
177
+
105
178
  def file_create(
106
179
  self,
107
- bucket_url: str | _pylinks.url.URL,
180
+ bucket_id: str,
108
181
  filepath: str | _Path,
109
- upload_path: str | None = None,
182
+ name: str | None = None,
110
183
  ) -> dict:
111
184
  """Upload a file to a Zenodo bucket.
112
185
 
186
+ Parameters
187
+ ----------
188
+ bucket_id
189
+ Bucket ID (e.g., `"d7524553-7f8c-4632-bffb-8bea6a90b88b"`)
190
+ or bucket URL (e.g., `"https://zenodo.org/api/files/d7524553-7f8c-4632-bffb-8bea6a90b88b"`)
191
+
113
192
  Returns
114
193
  -------
115
194
 
@@ -134,14 +213,20 @@ class Zenodo:
134
213
  }
135
214
  :::
136
215
  """
137
- if not isinstance(bucket_url, _pylinks.url.URL):
138
- bucket_url = _pylinks.url.create(bucket_url)
216
+ bucket_id = bucket_id.removeprefix(f"{self._url}/files/")
139
217
  filepath = _Path(filepath)
140
- upload_path = upload_path or filepath.name
218
+ name = name or filepath.name
141
219
  with open(filepath, "rb") as file:
142
220
  return self.rest_query(
143
- query=bucket_url / upload_path,
221
+ query=f"files/{bucket_id}/{name}",
144
222
  verb="PUT",
145
223
  data=file,
146
224
  content_type=None,
147
- )
225
+ )
226
+
227
+ def file_delete(self, deposition_id: str | int, file_id: str | int):
228
+ return self.rest_query(
229
+ f"deposit/depositions/{deposition_id}/files/{file_id}",
230
+ verb="DELETE",
231
+ response_type=None,
232
+ )
@@ -101,7 +101,7 @@ class GraphQLResponseError(WebAPIError):
101
101
  Exception class for GraphQL
102
102
  """
103
103
 
104
- def __init__(self, response: dict):
104
+ def __init__(self, response: dict, query: str):
105
105
  if "errors" in response:
106
106
  intro = "GraphQL response contains errors."
107
107
  elif "data" not in response:
@@ -109,10 +109,16 @@ class GraphQLResponseError(WebAPIError):
109
109
  super().__init__(
110
110
  title="GraphQL Response Error",
111
111
  intro=intro,
112
- details=_mdit.element.code_block(
113
- json.dumps(response, indent=3), language="json", caption="GraphQL Response"
114
- ),
112
+ details=_mdit.block_container(
113
+ _mdit.element.code_block(
114
+ json.dumps(response, indent=3), language="json", caption="GraphQL Response"
115
+ ),
116
+ _mdit.element.code_block(
117
+ query, language="graphql", caption="GraphQL Query"
118
+ ),
119
+ )
115
120
  )
121
+ self.query = query
116
122
  return
117
123
 
118
124
 
@@ -277,9 +277,9 @@ def graphql_query(
277
277
  if isinstance(response, dict):
278
278
  error_title = "GraphQL API Error"
279
279
  if "errors" in response:
280
- raise _exception.GraphQLResponseError(response)
280
+ raise _exception.GraphQLResponseError(response, query)
281
281
  elif "data" not in response:
282
- raise _exception.GraphQLResponseError(response)
282
+ raise _exception.GraphQLResponseError(response, query)
283
283
  else:
284
284
  response = response["data"]
285
285
  return response
@@ -1,3 +0,0 @@
1
- requests<3,>=2.31.0
2
- ExceptionMan==0.0.0.dev34
3
- MDit==0.0.0.dev34
File without changes
File without changes