currentsapi 0.1.0__tar.gz → 0.1.1__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.
- {currentsapi-0.1.0 → currentsapi-0.1.1}/PKG-INFO +1 -1
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi/__init__.py +1 -1
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi/client.py +24 -7
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi.egg-info/PKG-INFO +1 -1
- {currentsapi-0.1.0 → currentsapi-0.1.1}/tests/test_client.py +48 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/LICENSE.txt +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/README.md +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi/authentication.py +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi/constants.py +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi.egg-info/SOURCES.txt +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi.egg-info/dependency_links.txt +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi.egg-info/requires.txt +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/currentsapi.egg-info/top_level.txt +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/setup.cfg +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/setup.py +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/tests/test_company_news_monitor_example.py +0 -0
- {currentsapi-0.1.0 → currentsapi-0.1.1}/tests/test_source_linked_briefing_example.py +0 -0
|
@@ -11,19 +11,25 @@ class CurrentsAPIError(Exception):
|
|
|
11
11
|
|
|
12
12
|
def __init__(self, response):
|
|
13
13
|
self.response = response
|
|
14
|
-
|
|
14
|
+
self._status = response.get("status")
|
|
15
|
+
self._code = response.get("code")
|
|
16
|
+
self._message = response.get("message") or response.get("msg")
|
|
17
|
+
super().__init__(self._message or str(response))
|
|
15
18
|
|
|
16
19
|
@property
|
|
17
20
|
def status(self):
|
|
18
|
-
|
|
21
|
+
try:
|
|
22
|
+
return int(self._status)
|
|
23
|
+
except (TypeError, ValueError):
|
|
24
|
+
return self._status
|
|
19
25
|
|
|
20
26
|
@property
|
|
21
27
|
def code(self):
|
|
22
|
-
return self.
|
|
28
|
+
return self._code
|
|
23
29
|
|
|
24
30
|
@property
|
|
25
31
|
def message(self):
|
|
26
|
-
return self.
|
|
32
|
+
return self._message
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
class CurrentsAPI:
|
|
@@ -95,11 +101,11 @@ class CurrentsAPI:
|
|
|
95
101
|
params["category"] = category
|
|
96
102
|
|
|
97
103
|
if start_date:
|
|
98
|
-
date = self._parse_date(start_date, "start_date")
|
|
104
|
+
date = self._normalize_date(self._parse_date(start_date, "start_date"))
|
|
99
105
|
params["start_date"] = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
100
106
|
|
|
101
107
|
if end_date:
|
|
102
|
-
date = self._parse_date(end_date, "end_date")
|
|
108
|
+
date = self._normalize_date(self._parse_date(end_date, "end_date"))
|
|
103
109
|
params["end_date"] = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
104
110
|
|
|
105
111
|
return self._get(self.search_endpoint, params)
|
|
@@ -116,7 +122,12 @@ class CurrentsAPI:
|
|
|
116
122
|
@staticmethod
|
|
117
123
|
def _parse_date(date_value, param_name):
|
|
118
124
|
if isinstance(date_value, str):
|
|
119
|
-
|
|
125
|
+
try:
|
|
126
|
+
return parser.parse(date_value)
|
|
127
|
+
except (parser.ParserError, OverflowError, ValueError) as exc:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
"{} is not a parsable date: {}".format(param_name, exc)
|
|
130
|
+
) from exc
|
|
120
131
|
elif isinstance(date_value, datetime.date):
|
|
121
132
|
return date_value
|
|
122
133
|
else:
|
|
@@ -125,3 +136,9 @@ class CurrentsAPI:
|
|
|
125
136
|
param_name
|
|
126
137
|
)
|
|
127
138
|
)
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def _normalize_date(date_value):
|
|
142
|
+
if isinstance(date_value, datetime.datetime) and date_value.tzinfo is not None:
|
|
143
|
+
return date_value.astimezone(datetime.timezone.utc)
|
|
144
|
+
return date_value
|
|
@@ -160,6 +160,54 @@ class TestClient(unittest.TestCase):
|
|
|
160
160
|
with self.assertRaises(ValueError):
|
|
161
161
|
api.search(start_date=123)
|
|
162
162
|
|
|
163
|
+
@patch("currentsapi.client.requests.get")
|
|
164
|
+
def test_api_error_uses_msg_key(self, mock_get):
|
|
165
|
+
mock_get.return_value = Mock(
|
|
166
|
+
status_code=401,
|
|
167
|
+
json=Mock(return_value={"status": "401", "msg": "Invalid token"}),
|
|
168
|
+
)
|
|
169
|
+
api = CurrentsAPI("key")
|
|
170
|
+
with self.assertRaises(CurrentsAPIError) as ctx:
|
|
171
|
+
api.latest_news()
|
|
172
|
+
self.assertEqual(ctx.exception.status, 401)
|
|
173
|
+
self.assertIsNone(ctx.exception.code)
|
|
174
|
+
self.assertEqual(ctx.exception.message, "Invalid token")
|
|
175
|
+
self.assertEqual(str(ctx.exception), "Invalid token")
|
|
176
|
+
|
|
177
|
+
@patch("currentsapi.client.requests.get")
|
|
178
|
+
def test_api_error_prefers_message_over_msg(self, mock_get):
|
|
179
|
+
mock_get.return_value = Mock(
|
|
180
|
+
status_code=400,
|
|
181
|
+
json=Mock(
|
|
182
|
+
return_value={
|
|
183
|
+
"status": "400",
|
|
184
|
+
"msg": "Bad request",
|
|
185
|
+
"code": "INVALID_QUERY",
|
|
186
|
+
"message": "Invalid parameters",
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
api = CurrentsAPI("key")
|
|
191
|
+
with self.assertRaises(CurrentsAPIError) as ctx:
|
|
192
|
+
api.search(keywords="x", category="nope")
|
|
193
|
+
self.assertEqual(ctx.exception.status, 400)
|
|
194
|
+
self.assertEqual(ctx.exception.code, "INVALID_QUERY")
|
|
195
|
+
self.assertEqual(ctx.exception.message, "Invalid parameters")
|
|
196
|
+
self.assertEqual(str(ctx.exception), "Invalid parameters")
|
|
197
|
+
|
|
198
|
+
@patch("currentsapi.client.requests.get")
|
|
199
|
+
def test_tz_aware_datetime_converted_to_utc(self, mock_get):
|
|
200
|
+
mock_get.return_value = Mock(status_code=200, json=Mock(return_value={"status": "ok"}))
|
|
201
|
+
api = CurrentsAPI("key")
|
|
202
|
+
tz = datetime.timezone(datetime.timedelta(hours=8))
|
|
203
|
+
api.search(start_date=datetime.datetime(2024, 6, 1, 12, 0, tzinfo=tz))
|
|
204
|
+
kwargs = mock_get.call_args.kwargs
|
|
205
|
+
self.assertEqual(kwargs["params"]["start_date"], "2024-06-01T04:00:00Z")
|
|
206
|
+
|
|
207
|
+
def test_impossible_date_string_raises_valueerror(self):
|
|
208
|
+
api = CurrentsAPI("key")
|
|
209
|
+
with self.assertRaises(ValueError):
|
|
210
|
+
api.search(start_date="2026-13-45")
|
|
163
211
|
|
|
164
212
|
if __name__ == "__main__":
|
|
165
213
|
unittest.main()
|
|
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
|
|
File without changes
|
|
File without changes
|