ctftime-api 0.1.1__tar.gz → 0.1.2__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,12 +1,12 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ctftime_api
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: CTFTime API wrapper
5
5
  Keywords: ctftime,api,wrapper
6
6
  Author: John Toniutti
7
7
  Author-email: john.toniutti@gmail.com
8
8
  Requires-Python: >=3.11
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12
12
  Classifier: Framework :: Pydantic
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime
2
+ from typing import Any
2
3
 
3
4
  import httpx
4
5
  from httpx import URL, Timeout
@@ -9,7 +10,6 @@ from ctftime_api.models.vote import Vote
9
10
  from ctftime_api.models.event import Event, EventResult
10
11
  from ctftime_api.models.team import TeamRank, Team, TeamComplete
11
12
 
12
-
13
13
  __all__ = ["CTFTimeClient"]
14
14
 
15
15
 
@@ -27,6 +27,18 @@ class CTFTimeClient:
27
27
  self._client = httpx.AsyncClient(*args, **kwargs)
28
28
  self._base_url = URL("https://ctftime.org/api/v1/")
29
29
 
30
+ async def _get(self, url: str | URL, **kwargs) -> Any:
31
+ """
32
+ Perform a GET request.
33
+ :param url: The url to make the request to.
34
+ :param kwargs: Additional arguments to pass to the request.
35
+ :return: The response JSON.
36
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
37
+ """
38
+ response = await self._client.get(url, **kwargs)
39
+ response.raise_for_status()
40
+ return response.json()
41
+
30
42
  async def close(self):
31
43
  """Close the underlying httpx.AsyncClient."""
32
44
  await self._client.aclose()
@@ -39,16 +51,16 @@ class CTFTimeClient:
39
51
  :param year: The year to get the top teams for. If None, the current year will be used.
40
52
  :param limit: The number of teams to get.
41
53
  :return: A list of the top teams.
54
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
42
55
  """
43
56
  if year is None:
44
57
  url = self._base_url.join("top/")
45
58
  year = datetime.now().year
46
59
  else:
47
60
  url = self._base_url.join(f"top/{year}/")
48
- response = await self._client.get(url, params={"limit": limit})
49
- response.raise_for_status()
50
61
 
51
- teams = response.json().get(f"{year}", [])
62
+ response: dict[str, list[dict]] = await self._get(url, params={"limit": limit})
63
+ teams = response.get(f"{year}", [])
52
64
 
53
65
  return [TeamRank.model_validate(team) for team in teams]
54
66
 
@@ -60,6 +72,8 @@ class CTFTimeClient:
60
72
  :param country: The country to get the top teams for.
61
73
  It can be a pycountry Country object or a two-letter country code.
62
74
  :return: A list of the top teams.
75
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
76
+ :raise ValueError: If the country is not a two-letter country code or a pycountry Country object.
63
77
  """
64
78
  if isinstance(country, CountryAlpha2):
65
79
  country = country
@@ -70,10 +84,7 @@ class CTFTimeClient:
70
84
  )
71
85
 
72
86
  url = self._base_url.join("top-by-country/").join(f"{country}/")
73
- response = await self._client.get(url)
74
- response.raise_for_status()
75
-
76
- teams = response.json()
87
+ teams: list[dict[str, Any]] = await self._get(url)
77
88
 
78
89
  return [TeamRank.model_validate(team) for team in teams]
79
90
 
@@ -88,6 +99,8 @@ class CTFTimeClient:
88
99
  It can be a Unix timestamp or a datetime object.
89
100
  :param limit: The number of events to get.
90
101
  :return: A list of events.
102
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
103
+ :raise ValueError: If the start date is after the end date.
91
104
  """
92
105
  if isinstance(start, datetime):
93
106
  start = int(start.timestamp())
@@ -98,12 +111,9 @@ class CTFTimeClient:
98
111
  raise ValueError("The start date must be before the end date.")
99
112
 
100
113
  url = self._base_url.join("events/")
101
- response = await self._client.get(
114
+ events: list[dict[str, Any]] = await self._get(
102
115
  url, params={"start": start, "finish": end, "limit": limit}
103
116
  )
104
- response.raise_for_status()
105
-
106
- events = response.json()
107
117
 
108
118
  return [Event.model_validate(event) for event in events]
109
119
 
@@ -112,12 +122,10 @@ class CTFTimeClient:
112
122
  Get information about a specific event.
113
123
  :param event_id: The ID of the event.
114
124
  :return: The event information.
125
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
115
126
  """
116
127
  url = self._base_url.join(f"events/{event_id}/")
117
- response = await self._client.get(url)
118
- response.raise_for_status()
119
-
120
- event = response.json()
128
+ event: dict[str, Any] = await self._get(url)
121
129
 
122
130
  return Event.model_validate(event)
123
131
 
@@ -129,14 +137,14 @@ class CTFTimeClient:
129
137
  :param limit: The number of teams to get.
130
138
  :param offset: The offset to start from.
131
139
  :return: A list of teams.
140
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
132
141
  """
133
142
  url = self._base_url.join("teams/")
134
- response = await self._client.get(
143
+
144
+ response: dict[str, Any] = await self._get(
135
145
  url, params={"limit": limit, "offset": offset}
136
146
  )
137
- response.raise_for_status()
138
-
139
- teams = response.json().get("results", [])
147
+ teams: list[dict[str, Any]] = response.get("results", [])
140
148
 
141
149
  return [Team.model_validate(team) for team in teams]
142
150
 
@@ -145,12 +153,10 @@ class CTFTimeClient:
145
153
  Get information about a specific team.
146
154
  :param team_id: The ID of the team.
147
155
  :return: The team information.
156
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
148
157
  """
149
158
  url = self._base_url.join(f"teams/{team_id}/")
150
- response = await self._client.get(url)
151
- response.raise_for_status()
152
-
153
- team = response.json()
159
+ team: dict[str, Any] = await self._get(url)
154
160
 
155
161
  return TeamComplete.model_validate(team)
156
162
 
@@ -162,19 +168,17 @@ class CTFTimeClient:
162
168
  :param year: The year to get the results for.
163
169
  If None, the current year will be used.
164
170
  :return: A dictionary of event results.
171
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
165
172
  """
166
173
  if year is None:
167
174
  url = self._base_url.join("results/")
168
175
  else:
169
176
  url = self._base_url.join(f"results/{year}/")
170
177
 
171
- response = await self._client.get(url)
172
- response.raise_for_status()
173
-
174
- event = response.json()
178
+ event: dict[str, dict] = await self._get(url)
175
179
 
176
180
  return {
177
- int(ctf_id): EventResult(**result, ctf_id=ctf_id)
181
+ int(ctf_id): EventResult(**result, ctf_id=int(ctf_id))
178
182
  for ctf_id, result in event.items()
179
183
  }
180
184
 
@@ -188,6 +192,7 @@ class CTFTimeClient:
188
192
  :param timeout: The timeout for the request.
189
193
  If None, the session timeout will be used.
190
194
  :return: A list of votes.
195
+ :raise httpx.HTTPStatusError: If the response status code is not successful.
191
196
  """
192
197
  if year is None:
193
198
  year = datetime.now().year
@@ -196,9 +201,6 @@ class CTFTimeClient:
196
201
  timeout = self._client.timeout
197
202
 
198
203
  url = self._base_url.join(f"votes/{year}/")
199
- response = await self._client.get(url, timeout=timeout)
200
- response.raise_for_status()
201
-
202
- votes = response.json()
204
+ votes: list[dict] = await self._get(url, timeout=timeout)
203
205
 
204
206
  return [Vote(**vote) for vote in votes]
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "ctftime_api"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "CTFTime API wrapper"
5
5
  authors = [
6
6
  {name = "John Toniutti",email = "john.toniutti@gmail.com"}
7
7
  ]
8
8
  keywords = ["ctftime", "api", "wrapper"]
9
9
  classifiers = [
10
- "Development Status :: 3 - Alpha",
10
+ "Development Status :: 4 - Beta",
11
11
  "Intended Audience :: Developers",
12
12
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
13
13
  "Framework :: Pydantic",
File without changes
File without changes