python-weather 2.0.5__tar.gz → 2.0.7__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.
- {python_weather-2.0.5/python_weather.egg-info → python_weather-2.0.7}/PKG-INFO +1 -1
- {python_weather-2.0.5 → python_weather-2.0.7}/pyproject.toml +1 -1
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/__init__.py +1 -1
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/base.py +3 -4
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/client.py +22 -16
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/enums.py +15 -21
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/errors.py +2 -2
- {python_weather-2.0.5 → python_weather-2.0.7/python_weather.egg-info}/PKG-INFO +1 -1
- {python_weather-2.0.5 → python_weather-2.0.7}/LICENSE +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/MANIFEST.in +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/README.md +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/constants.py +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather/forecast.py +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather.egg-info/SOURCES.txt +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather.egg-info/dependency_links.txt +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather.egg-info/requires.txt +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/python_weather.egg-info/top_level.txt +0 -0
- {python_weather-2.0.5 → python_weather-2.0.7}/setup.cfg +0 -0
|
@@ -22,7 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
22
22
|
SOFTWARE.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
from enum import auto
|
|
26
25
|
from typing import Tuple
|
|
27
26
|
|
|
28
27
|
from .errors import Error
|
|
@@ -38,7 +37,7 @@ class CustomizableBase:
|
|
|
38
37
|
self.locale = locale
|
|
39
38
|
|
|
40
39
|
@property
|
|
41
|
-
def unit(self) ->
|
|
40
|
+
def unit(self) -> _Unit:
|
|
42
41
|
"""The measuring unit used to display information in this object."""
|
|
43
42
|
|
|
44
43
|
return self.__unit
|
|
@@ -68,9 +67,9 @@ class CustomizableBase:
|
|
|
68
67
|
"""
|
|
69
68
|
Sets the default localization used to display information in this object.
|
|
70
69
|
|
|
71
|
-
:param to: The new :class
|
|
70
|
+
:param to: The new :class:`~python_weather.enums.Locale` to be used to display information in this object.
|
|
72
71
|
:type to: Locale
|
|
73
|
-
:exception Error: If the ``to`` argument is not a part of the :class
|
|
72
|
+
:exception Error: If the ``to`` argument is not a part of the :class:`~python_weather.enums.Locale` enum.
|
|
74
73
|
"""
|
|
75
74
|
|
|
76
75
|
if not isinstance(to, Locale):
|
|
@@ -26,7 +26,6 @@ from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
|
26
26
|
from urllib.parse import quote_plus
|
|
27
27
|
from typing import Optional, Tuple
|
|
28
28
|
from asyncio import sleep
|
|
29
|
-
from enum import auto
|
|
30
29
|
|
|
31
30
|
from .errors import Error, RequestError
|
|
32
31
|
from .constants import _Unit, METRIC
|
|
@@ -40,23 +39,26 @@ class Client(CustomizableBase):
|
|
|
40
39
|
The class that lets you interact with the API.
|
|
41
40
|
|
|
42
41
|
:param unit: Whether to use the metric or imperial/customary system (``IMPERIAL``). Defaults to ``METRIC``.
|
|
43
|
-
:type unit:
|
|
42
|
+
:type unit: _Unit
|
|
44
43
|
:param locale: Whether to use a different locale/language as the description for the returned forecast. Defaults to ``Locale.ENGLISH``.
|
|
45
|
-
:type locale:
|
|
46
|
-
:param session: Whether to use an existing aiohttp client session for requesting or not. Defaults to ``None`` (creates a new one instead)
|
|
47
|
-
:type session: Optional[:class
|
|
44
|
+
:type locale: Locale
|
|
45
|
+
:param session: Whether to use an existing aiohttp client session for requesting or not. Defaults to ``None`` (creates a new one instead).
|
|
46
|
+
:type session: Optional[:class:`~aiohttp.ClientSession`]
|
|
47
|
+
:param max_retries: Maximum amount of retries upon receiving HTTP request failure before raising a :class:`~python_weather.errors.RequestError`. To have infinite retries, use ``-1`` (NOT recommended). Defaults to ``None`` (or 3 retries).
|
|
48
|
+
:type max_retries: Optional[:class:`int`]
|
|
48
49
|
|
|
49
|
-
:raises Error: If ``unit`` is not ``METRIC`` or ``IMPERIAL``, or if ``locale`` is not ``None`` and not a part of the :class
|
|
50
|
+
:raises Error: If ``unit`` is not ``METRIC`` or ``IMPERIAL``, or if ``locale`` is not ``None`` and not a part of the :class:`~python_weather.enums.Locale` enum.
|
|
50
51
|
"""
|
|
51
52
|
|
|
52
|
-
__slots__: Tuple[str, ...] = ('__own_session', '__session')
|
|
53
|
+
__slots__: Tuple[str, ...] = ('__own_session', '__session', '__max_retries')
|
|
53
54
|
|
|
54
55
|
def __init__(
|
|
55
56
|
self,
|
|
56
57
|
*,
|
|
57
|
-
unit:
|
|
58
|
-
locale:
|
|
58
|
+
unit: _Unit = METRIC,
|
|
59
|
+
locale: Locale = Locale.ENGLISH,
|
|
59
60
|
session: Optional[ClientSession] = None,
|
|
61
|
+
max_retries: Optional[int] = None,
|
|
60
62
|
):
|
|
61
63
|
super().__init__(unit, locale)
|
|
62
64
|
|
|
@@ -65,6 +67,7 @@ class Client(CustomizableBase):
|
|
|
65
67
|
timeout=ClientTimeout(total=5000.0),
|
|
66
68
|
connector=TCPConnector(verify_ssl=False),
|
|
67
69
|
)
|
|
70
|
+
self.__max_retries = max_retries or 3
|
|
68
71
|
|
|
69
72
|
def __repr__(self) -> str:
|
|
70
73
|
return f'<{__class__.__name__} {self.__session!r}>'
|
|
@@ -73,7 +76,7 @@ class Client(CustomizableBase):
|
|
|
73
76
|
self,
|
|
74
77
|
location: str,
|
|
75
78
|
*,
|
|
76
|
-
unit: Optional[
|
|
79
|
+
unit: Optional[_Unit] = None,
|
|
77
80
|
locale: Optional[Locale] = None,
|
|
78
81
|
) -> Forecast:
|
|
79
82
|
"""
|
|
@@ -82,11 +85,12 @@ class Client(CustomizableBase):
|
|
|
82
85
|
:param location: The requested location name for said weather forecast.
|
|
83
86
|
:type location: str
|
|
84
87
|
:param unit: Overrides the unit used by this object. Defaults to the one used by this object.
|
|
85
|
-
:type unit: Optional[
|
|
88
|
+
:type unit: Optional[``_Unit``]
|
|
86
89
|
:param locale: Overrides the locale used by this object. Defaults to the one used by this object.
|
|
87
|
-
:type locale: Optional[Locale]
|
|
90
|
+
:type locale: Optional[:class:`~python_weather.enums.Locale`]
|
|
88
91
|
|
|
89
|
-
:exception Error: If the aiohttp client session used by the :class
|
|
92
|
+
:exception Error: If the aiohttp client session used by the :class:`~python_weather.client.Client` object is already closed, if the ``unit`` argument is not ``None`` and it's also not ``METRIC`` or ``IMPERIAL``, if the ``locale`` argument is not ``None`` and it's also not a part of the :class:`~python_weather.enums.Locale` enum.
|
|
93
|
+
:exception RequestError: If the :class:`~python_weather.client.Client` can't send a web request to the web server.
|
|
90
94
|
|
|
91
95
|
:returns: The requested weather forecast.
|
|
92
96
|
:rtype: Forecast
|
|
@@ -105,6 +109,7 @@ class Client(CustomizableBase):
|
|
|
105
109
|
|
|
106
110
|
subdomain = f'{locale.value}.' if locale != Locale.ENGLISH else ''
|
|
107
111
|
delay = 0.5
|
|
112
|
+
attempts = 0
|
|
108
113
|
|
|
109
114
|
while True:
|
|
110
115
|
try:
|
|
@@ -115,14 +120,15 @@ class Client(CustomizableBase):
|
|
|
115
120
|
|
|
116
121
|
return Forecast(await resp.json(), unit, locale)
|
|
117
122
|
except Exception as err:
|
|
118
|
-
if
|
|
123
|
+
if attempts == self.__max_retries:
|
|
119
124
|
raise RequestError(err)
|
|
120
125
|
|
|
121
126
|
await sleep(delay)
|
|
127
|
+
attempts += 1
|
|
122
128
|
delay *= 2
|
|
123
129
|
|
|
124
130
|
async def close(self) -> None:
|
|
125
|
-
"""Closes the :class
|
|
131
|
+
"""Closes the :class:`~python_weather.client.Client` object. Nothing will happen if the client uses a pre-existing :class:`~aiohttp.ClientSession` or if the session is already closed."""
|
|
126
132
|
|
|
127
133
|
if self.__own_session and not self.__session.closed:
|
|
128
134
|
await self.__session.close()
|
|
@@ -130,5 +136,5 @@ class Client(CustomizableBase):
|
|
|
130
136
|
async def __aenter__(self) -> 'Client':
|
|
131
137
|
return self
|
|
132
138
|
|
|
133
|
-
async def __aexit__(self, *_, **__):
|
|
139
|
+
async def __aexit__(self, *_, **__) -> None:
|
|
134
140
|
await self.close()
|
|
@@ -186,14 +186,17 @@ class WindDirection(BasicEnum):
|
|
|
186
186
|
else:
|
|
187
187
|
return 326.25 < other <= 348.75
|
|
188
188
|
|
|
189
|
+
def __int__(self) -> int:
|
|
190
|
+
return int(self.degrees)
|
|
191
|
+
|
|
189
192
|
def __float__(self) -> float:
|
|
190
|
-
return self.
|
|
193
|
+
return self.degrees
|
|
191
194
|
|
|
192
195
|
@property
|
|
193
196
|
def emoji(self) -> str:
|
|
194
197
|
"""The emoji representing this enum."""
|
|
195
198
|
|
|
196
|
-
return WIND_DIRECTION_EMOJIS[int(((self.
|
|
199
|
+
return WIND_DIRECTION_EMOJIS[int(((self.degrees + 22.5) % 360) / 45.0)]
|
|
197
200
|
|
|
198
201
|
|
|
199
202
|
class Locale(Enum):
|
|
@@ -308,38 +311,29 @@ class Kind(BasicEnum):
|
|
|
308
311
|
|
|
309
312
|
@classmethod
|
|
310
313
|
def _missing_(self, value: int) -> 'Kind':
|
|
311
|
-
if value
|
|
314
|
+
if value in (248, 260):
|
|
312
315
|
return self.FOG
|
|
313
|
-
elif value
|
|
316
|
+
elif value in (263, 353):
|
|
314
317
|
return self.LIGHT_SHOWERS
|
|
315
|
-
elif value
|
|
318
|
+
elif value in (362, 365, 374):
|
|
316
319
|
return self.LIGHT_SLEET_SHOWERS
|
|
317
|
-
elif (
|
|
318
|
-
value == 185
|
|
319
|
-
or value == 281
|
|
320
|
-
or value == 284
|
|
321
|
-
or value == 311
|
|
322
|
-
or value == 314
|
|
323
|
-
or value == 317
|
|
324
|
-
or value == 350
|
|
325
|
-
or value == 377
|
|
326
|
-
):
|
|
320
|
+
elif value in (185, 281, 284, 311, 314, 317, 350, 377):
|
|
327
321
|
return self.LIGHT_SLEET
|
|
328
322
|
elif value == 386:
|
|
329
323
|
return self.THUNDERY_SHOWERS
|
|
330
324
|
elif value == 320:
|
|
331
325
|
return self.LIGHT_SNOW
|
|
332
|
-
elif value
|
|
326
|
+
elif value in (329, 332, 338):
|
|
333
327
|
return self.HEAVY_SNOW
|
|
334
|
-
elif value
|
|
328
|
+
elif value in (293, 296):
|
|
335
329
|
return self.LIGHT_RAIN
|
|
336
|
-
elif value
|
|
330
|
+
elif value in (305, 356):
|
|
337
331
|
return self.HEAVY_SHOWERS
|
|
338
|
-
elif value
|
|
332
|
+
elif value in (308, 359):
|
|
339
333
|
return self.HEAVY_RAIN
|
|
340
|
-
elif value
|
|
334
|
+
elif value in (326, 368):
|
|
341
335
|
return self.LIGHT_SNOW_SHOWERS
|
|
342
|
-
elif value
|
|
336
|
+
elif value in (371, 395):
|
|
343
337
|
return self.HEAVY_SNOW_SHOWERS
|
|
344
338
|
|
|
345
339
|
@property
|
|
@@ -32,12 +32,12 @@ class Error(Exception):
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
class RequestError(Error):
|
|
35
|
-
"""Thrown upon HTTP request failure. Extends :class
|
|
35
|
+
"""Thrown upon HTTP request failure. Extends :class:`~python_weather.errors.Error`."""
|
|
36
36
|
|
|
37
37
|
__slots__: Tuple[str, ...] = ('source',)
|
|
38
38
|
|
|
39
39
|
source: Exception
|
|
40
|
-
"""The :class:`Exception` instance causing this exception."""
|
|
40
|
+
"""The :py:class:`Exception` instance causing this exception."""
|
|
41
41
|
|
|
42
42
|
def __init__(self, source: Exception):
|
|
43
43
|
self.source = source
|
|
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
|