python-weather 1.1.2__tar.gz → 2.0.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.
- {python-weather-1.1.2 → python-weather-2.0.1}/MANIFEST.in +2 -1
- {python-weather-1.1.2/python_weather.egg-info → python-weather-2.0.1}/PKG-INFO +10 -19
- {python-weather-1.1.2 → python-weather-2.0.1}/README.md +5 -13
- {python-weather-1.1.2 → python-weather-2.0.1}/pyproject.toml +5 -6
- python-weather-2.0.1/python_weather/__init__.py +18 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/base.py +59 -63
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/client.py +29 -28
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/constants.py +11 -5
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/enums.py +139 -95
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/errors.py +2 -2
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather/forecast.py +189 -257
- {python-weather-1.1.2 → python-weather-2.0.1/python_weather.egg-info}/PKG-INFO +10 -19
- python-weather-1.1.2/python_weather/__init__.py +0 -14
- {python-weather-1.1.2 → python-weather-2.0.1}/LICENSE +0 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather.egg-info/SOURCES.txt +0 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather.egg-info/dependency_links.txt +0 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather.egg-info/requires.txt +0 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/python_weather.egg-info/top_level.txt +0 -0
- {python-weather-1.1.2 → python-weather-2.0.1}/setup.cfg +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-weather
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A free and asynchronous weather API wrapper made in Python, for Python.
|
|
5
5
|
Author: null8626
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL:
|
|
8
|
-
Project-URL:
|
|
9
|
-
Project-URL:
|
|
10
|
-
Project-URL:
|
|
11
|
-
Project-URL: download_url, https://github.com/null8626/python-weather/archive/1.1.2.tar.gz
|
|
7
|
+
Project-URL: Changelog, https://python-weather.readthedocs.io/en/latest/changelog.html
|
|
8
|
+
Project-URL: Homepage, https://python-weather.readthedocs.io/en/latest/
|
|
9
|
+
Project-URL: Documentation, https://python-weather.readthedocs.io/en/latest/
|
|
10
|
+
Project-URL: Repository, https://github.com/null8626/python-weather
|
|
12
11
|
Keywords: weather,forecast,weather-api,weather-forecast
|
|
13
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
13
|
Classifier: Framework :: aiohttp
|
|
@@ -36,19 +35,11 @@ Description-Content-Type: text/markdown
|
|
|
36
35
|
License-File: LICENSE
|
|
37
36
|
Requires-Dist: aiohttp==3.9.3
|
|
38
37
|
|
|
39
|
-
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url]
|
|
38
|
+
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url]
|
|
40
39
|
|
|
41
40
|
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
42
41
|
[pypi-url]: https://pypi.org/project/python-weather/
|
|
43
42
|
[downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
|
|
44
|
-
[ci-image]: https://github.com/null8626/python-weather/workflows/CI/badge.svg
|
|
45
|
-
[ci-url]: https://github.com/null8626/python-weather/actions/workflows/CI.yml
|
|
46
|
-
[doc-ci-image]: https://readthedocs.org/projects/python-weather/badge/?version=latest
|
|
47
|
-
[doc-ci-url]: https://python-weather.readthedocs.io/en/latest/?badge=latest
|
|
48
|
-
[github-license-image]: https://img.shields.io/github/license/null8626/python-weather?style=flat-square
|
|
49
|
-
[github-license-url]: https://github.com/null8626/python-weather/blob/main/LICENSE
|
|
50
|
-
[blazingly-fast-image]: https://img.shields.io/badge/speed-BLAZINGLY%20FAST!!!%20%F0%9F%94%A5%F0%9F%9A%80%F0%9F%92%AA%F0%9F%98%8E-brightgreen.svg?style=flat-square
|
|
51
|
-
[blazingly-fast-url]: https://twitter.com/acdlite/status/974390255393505280
|
|
52
43
|
|
|
53
44
|
A free and asynchronous weather Python API wrapper made in Python, for Python.
|
|
54
45
|
|
|
@@ -76,14 +67,14 @@ async def getweather():
|
|
|
76
67
|
weather = await client.get('New York')
|
|
77
68
|
|
|
78
69
|
# returns the current day's forecast temperature (int)
|
|
79
|
-
print(weather.
|
|
70
|
+
print(weather.temperature)
|
|
80
71
|
|
|
81
72
|
# get the weather forecast for a few days
|
|
82
|
-
for
|
|
83
|
-
print(
|
|
73
|
+
for daily in weather.daily_forecasts:
|
|
74
|
+
print(daily)
|
|
84
75
|
|
|
85
76
|
# hourly forecasts
|
|
86
|
-
for hourly in
|
|
77
|
+
for hourly in daily.hourly_forecasts:
|
|
87
78
|
print(f' --> {hourly!r}')
|
|
88
79
|
|
|
89
80
|
if __name__ == '__main__':
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url]
|
|
1
|
+
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url]
|
|
2
2
|
|
|
3
3
|
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
4
4
|
[pypi-url]: https://pypi.org/project/python-weather/
|
|
5
5
|
[downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
|
|
6
|
-
[ci-image]: https://github.com/null8626/python-weather/workflows/CI/badge.svg
|
|
7
|
-
[ci-url]: https://github.com/null8626/python-weather/actions/workflows/CI.yml
|
|
8
|
-
[doc-ci-image]: https://readthedocs.org/projects/python-weather/badge/?version=latest
|
|
9
|
-
[doc-ci-url]: https://python-weather.readthedocs.io/en/latest/?badge=latest
|
|
10
|
-
[github-license-image]: https://img.shields.io/github/license/null8626/python-weather?style=flat-square
|
|
11
|
-
[github-license-url]: https://github.com/null8626/python-weather/blob/main/LICENSE
|
|
12
|
-
[blazingly-fast-image]: https://img.shields.io/badge/speed-BLAZINGLY%20FAST!!!%20%F0%9F%94%A5%F0%9F%9A%80%F0%9F%92%AA%F0%9F%98%8E-brightgreen.svg?style=flat-square
|
|
13
|
-
[blazingly-fast-url]: https://twitter.com/acdlite/status/974390255393505280
|
|
14
6
|
|
|
15
7
|
A free and asynchronous weather Python API wrapper made in Python, for Python.
|
|
16
8
|
|
|
@@ -38,14 +30,14 @@ async def getweather():
|
|
|
38
30
|
weather = await client.get('New York')
|
|
39
31
|
|
|
40
32
|
# returns the current day's forecast temperature (int)
|
|
41
|
-
print(weather.
|
|
33
|
+
print(weather.temperature)
|
|
42
34
|
|
|
43
35
|
# get the weather forecast for a few days
|
|
44
|
-
for
|
|
45
|
-
print(
|
|
36
|
+
for daily in weather.daily_forecasts:
|
|
37
|
+
print(daily)
|
|
46
38
|
|
|
47
39
|
# hourly forecasts
|
|
48
|
-
for hourly in
|
|
40
|
+
for hourly in daily.hourly_forecasts:
|
|
49
41
|
print(f' --> {hourly!r}')
|
|
50
42
|
|
|
51
43
|
if __name__ == '__main__':
|
|
@@ -3,7 +3,7 @@ requires = ["setuptools"]
|
|
|
3
3
|
|
|
4
4
|
[project]
|
|
5
5
|
name = "python-weather"
|
|
6
|
-
version = "
|
|
6
|
+
version = "2.0.1"
|
|
7
7
|
description = "A free and asynchronous weather API wrapper made in Python, for Python."
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
license = { text = "MIT" }
|
|
@@ -36,8 +36,7 @@ classifiers = [
|
|
|
36
36
|
requires-python = ">=3.8"
|
|
37
37
|
|
|
38
38
|
[project.urls]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
download_url = "https://github.com/null8626/python-weather/archive/1.1.2.tar.gz"
|
|
39
|
+
Changelog = "https://python-weather.readthedocs.io/en/latest/changelog.html"
|
|
40
|
+
Homepage = "https://python-weather.readthedocs.io/en/latest/"
|
|
41
|
+
Documentation = "https://python-weather.readthedocs.io/en/latest/"
|
|
42
|
+
Repository = "https://github.com/null8626/python-weather"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
|
|
2
|
+
from .constants import METRIC, IMPERIAL
|
|
3
|
+
from .client import Client
|
|
4
|
+
from .errors import Error
|
|
5
|
+
|
|
6
|
+
__version__ = '2.0.1'
|
|
7
|
+
__all__ = (
|
|
8
|
+
'METRIC',
|
|
9
|
+
'IMPERIAL',
|
|
10
|
+
'Client',
|
|
11
|
+
'Error',
|
|
12
|
+
'HeatIndex',
|
|
13
|
+
'Kind',
|
|
14
|
+
'Locale',
|
|
15
|
+
'Phase',
|
|
16
|
+
'UltraViolet',
|
|
17
|
+
'WindDirection',
|
|
18
|
+
)
|
|
@@ -1,141 +1,137 @@
|
|
|
1
|
-
from enum import auto
|
|
1
|
+
from enum import auto
|
|
2
2
|
|
|
3
|
-
from .
|
|
3
|
+
from .errors import Error
|
|
4
|
+
from .enums import WindDirection, Kind, Locale, UltraViolet
|
|
4
5
|
from .constants import _Unit
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
class CustomizableBase:
|
|
7
9
|
__slots__ = ('__unit', '__locale')
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
def __init__(self, unit: _Unit, locale: Locale):
|
|
10
12
|
self.unit = unit
|
|
11
13
|
self.locale = locale
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
@property
|
|
14
16
|
def unit(self) -> auto:
|
|
15
17
|
"""The measuring unit used to display information in this object."""
|
|
16
|
-
|
|
18
|
+
|
|
17
19
|
return self.__unit
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
@unit.setter
|
|
20
22
|
def unit(self, to: _Unit):
|
|
21
23
|
"""
|
|
22
24
|
Sets the default measuring unit used to display information in this object.
|
|
23
|
-
|
|
25
|
+
|
|
24
26
|
:param to: The new default measuring unit to be used to display information in this object. Must be either ``METRIC`` or ``IMPERIAL``.
|
|
25
27
|
:exception Error: If the ``to`` argument is not either ``METRIC`` or ``IMPERIAL``.
|
|
26
28
|
"""
|
|
27
|
-
|
|
29
|
+
|
|
28
30
|
if not isinstance(to, _Unit):
|
|
29
31
|
raise Error('Invalid measuring unit specified!')
|
|
30
|
-
|
|
32
|
+
|
|
31
33
|
self.__unit = to
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
@property
|
|
34
36
|
def locale(self) -> Locale:
|
|
35
37
|
"""The localization used to display information in this object."""
|
|
36
|
-
|
|
38
|
+
|
|
37
39
|
return self.__locale
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
@locale.setter
|
|
40
42
|
def locale(self, to: Locale):
|
|
41
43
|
"""
|
|
42
44
|
Sets the default localization used to display information in this object.
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
:param to: The new :class:`Locale` to be used to display information in this object.
|
|
45
47
|
:type to: Locale
|
|
46
48
|
:exception Error: If the ``to`` argument is not a part of the :class:`Locale` enum.
|
|
47
49
|
"""
|
|
48
|
-
|
|
50
|
+
|
|
49
51
|
if not isinstance(to, Locale):
|
|
50
52
|
raise Error(f'Expected {to!r} to be a Locale enum')
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
self.__locale = to
|
|
53
55
|
|
|
56
|
+
|
|
54
57
|
class BaseForecast(CustomizableBase):
|
|
55
58
|
__slots__ = ('__inner',)
|
|
56
|
-
|
|
59
|
+
|
|
57
60
|
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
58
61
|
self.__inner = json
|
|
59
|
-
|
|
62
|
+
|
|
60
63
|
super().__init__(unit, locale)
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
@property
|
|
63
|
-
def ultraviolet(self) ->
|
|
64
|
-
"""The
|
|
65
|
-
|
|
66
|
-
return
|
|
67
|
-
|
|
66
|
+
def ultraviolet(self) -> UltraViolet:
|
|
67
|
+
"""The ultra-violet (UV) index."""
|
|
68
|
+
|
|
69
|
+
return UltraViolet._new(int(self.__inner['uvIndex']))
|
|
70
|
+
|
|
68
71
|
@property
|
|
69
72
|
def feels_like(self) -> int:
|
|
70
73
|
"""What it felt like, in Celcius or Fahrenheit."""
|
|
71
|
-
|
|
72
|
-
return int(
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
74
|
+
|
|
75
|
+
return int(self.__inner[f'FeelsLike{self._CustomizableBase__unit.temperature}'])
|
|
76
|
+
|
|
76
77
|
@property
|
|
77
78
|
def humidity(self) -> int:
|
|
78
79
|
"""The humidity value in percent."""
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
return int(self.__inner['humidity'])
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
@property
|
|
83
84
|
def temperature(self) -> int:
|
|
84
|
-
"""The
|
|
85
|
-
|
|
85
|
+
"""The temperature in either Celcius or Fahrenheit."""
|
|
86
|
+
|
|
86
87
|
return int(self.__inner[f'temp_{self._CustomizableBase__unit.temperature}'])
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
@property
|
|
89
90
|
def precipitation(self) -> float:
|
|
90
91
|
"""The precipitation in either Millimeters or Inches."""
|
|
91
|
-
|
|
92
|
-
return float(
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
|
|
92
|
+
|
|
93
|
+
return float(self.__inner[f'precip{self._CustomizableBase__unit.precipitation}'])
|
|
94
|
+
|
|
96
95
|
@property
|
|
97
96
|
def pressure(self) -> float:
|
|
98
97
|
"""The pressure in either Pascal or Inches."""
|
|
99
|
-
|
|
100
|
-
return float(
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
98
|
+
|
|
99
|
+
return float(self.__inner[f'pressure{self._CustomizableBase__unit.pressure}'])
|
|
100
|
+
|
|
104
101
|
@property
|
|
105
102
|
def visibility(self) -> int:
|
|
106
103
|
"""The visibility distance in either Kilometers or Miles."""
|
|
107
|
-
|
|
108
|
-
return int(
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
|
|
104
|
+
|
|
105
|
+
return int(self.__inner[f'visibility{self._CustomizableBase__unit.visibility}'])
|
|
106
|
+
|
|
112
107
|
@property
|
|
113
108
|
def wind_speed(self) -> int:
|
|
114
109
|
"""The wind speeds in either Kilometers per hour or Miles per hour."""
|
|
115
|
-
|
|
116
|
-
return int(
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
|
|
110
|
+
|
|
111
|
+
return int(self.__inner[f'windspeed{self._CustomizableBase__unit.velocity}'])
|
|
112
|
+
|
|
120
113
|
@property
|
|
121
114
|
def wind_direction(self) -> WindDirection:
|
|
122
115
|
"""The wind direction."""
|
|
123
|
-
|
|
116
|
+
|
|
124
117
|
return WindDirection._new(
|
|
125
118
|
self.__inner['winddir16Point'], int(self.__inner['winddirDegree'])
|
|
126
119
|
)
|
|
127
|
-
|
|
120
|
+
|
|
128
121
|
@property
|
|
129
122
|
def description(self) -> str:
|
|
130
123
|
"""The description regarding the forecast. This can be localized in different languages depending on the localization used."""
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'value'
|
|
134
|
-
|
|
135
|
-
f'lang_{self._CustomizableBase__locale.value}'][0]['value']
|
|
136
|
-
|
|
124
|
+
|
|
125
|
+
description = (
|
|
126
|
+
self.__inner['weatherDesc'][0]['value']
|
|
127
|
+
if self._CustomizableBase__locale == Locale.ENGLISH
|
|
128
|
+
else self.__inner[f'lang_{self._CustomizableBase__locale.value}'][0]['value']
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return description.strip()
|
|
132
|
+
|
|
137
133
|
@property
|
|
138
134
|
def kind(self) -> Kind:
|
|
139
|
-
"""The kind of the
|
|
140
|
-
|
|
135
|
+
"""The kind of the forecast."""
|
|
136
|
+
|
|
141
137
|
return Kind(int(self.__inner['weatherCode']))
|
|
@@ -6,104 +6,105 @@ from enum import auto
|
|
|
6
6
|
|
|
7
7
|
from .constants import _Unit, METRIC
|
|
8
8
|
from .base import CustomizableBase
|
|
9
|
-
from .forecast import
|
|
9
|
+
from .forecast import Forecast
|
|
10
10
|
from .errors import Error
|
|
11
11
|
from .enums import Locale
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
class Client(CustomizableBase):
|
|
14
15
|
"""
|
|
15
16
|
The class that lets you interact with the API.
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
:param unit: Whether to use the metric or imperial/customary system (``IMPERIAL``). Defaults to ``METRIC``.
|
|
18
19
|
:type unit: Optional[:py:class:`enum.auto`]
|
|
19
|
-
:param locale: Whether to use a different locale/language as the description for the returned
|
|
20
|
+
:param locale: Whether to use a different locale/language as the description for the returned forecast. Defaults to ``Locale.ENGLISH``.
|
|
20
21
|
:type locale: Optional[Locale]
|
|
21
22
|
:param session: Whether to use an existing aiohttp client session for requesting or not. Defaults to ``None`` (creates a new one instead)
|
|
22
23
|
:type session: Optional[:class:`aiohttp.ClientSession`]
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
:raises Error: If the ``unit`` argument is not ``None`` and it's also not ``METRIC`` or ``IMPERIAL``, or if the ``locale`` argument is not ``None`` and it's also not a part of the :class:`Locale` enum.
|
|
25
26
|
"""
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
__slots__ = ('__session',)
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
def __init__(
|
|
30
31
|
self,
|
|
31
32
|
*,
|
|
32
33
|
unit: Optional[auto] = METRIC,
|
|
33
34
|
locale: Optional[Locale] = Locale.ENGLISH,
|
|
34
|
-
session: Optional[ClientSession] = None
|
|
35
|
+
session: Optional[ClientSession] = None,
|
|
35
36
|
):
|
|
36
37
|
super().__init__(unit, locale)
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
self.__session = session or ClientSession(
|
|
39
40
|
timeout=ClientTimeout(total=5000.0),
|
|
40
|
-
connector=TCPConnector(verify_ssl=False)
|
|
41
|
+
connector=TCPConnector(verify_ssl=False),
|
|
41
42
|
)
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
def __repr__(self) -> str:
|
|
44
45
|
return f'<{self.__class__.__name__} {self.__session!r}>'
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
async def get(
|
|
47
48
|
self,
|
|
48
49
|
location: str,
|
|
49
50
|
*,
|
|
50
51
|
unit: Optional[auto] = None,
|
|
51
|
-
locale: Optional[Locale] = None
|
|
52
|
-
) ->
|
|
52
|
+
locale: Optional[Locale] = None,
|
|
53
|
+
) -> Forecast:
|
|
53
54
|
"""
|
|
54
55
|
Fetches a weather forecast for a specific location.
|
|
55
|
-
|
|
56
|
+
|
|
56
57
|
:param location: The requested location name for said weather forecast.
|
|
57
58
|
:type location: str
|
|
58
59
|
:param unit: Overrides the metric or imperial/customary system (``IMPERIAL``) used by the :class:`Client` object. Defaults to ``None`` (uses the one from the :class:`Client`).
|
|
59
60
|
:type unit: Optional[:py:class:`enum.auto`]
|
|
60
61
|
:param locale: Overrides the locale/language used by the :class:`Client` object. Defaults to ``None`` (uses the one from the :class:`Client`).
|
|
61
62
|
:type locale: Optional[Locale]
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
:exception Error: If the aiohttp client session used by the :class:`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:`Locale` enum, or if the :class:`Client` cannot send a web request to the web server.
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
:returns: The requested weather forecast.
|
|
66
|
-
:rtype:
|
|
67
|
+
:rtype: Forecast
|
|
67
68
|
"""
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
if (not isinstance(location, str)) or (not location):
|
|
70
71
|
raise Error(f'Expected a proper location str, got {location!r}')
|
|
71
72
|
elif self.__session.closed:
|
|
72
73
|
raise Error('Client is already closed')
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
if not isinstance(unit, _Unit):
|
|
75
76
|
unit = self._CustomizableBase__unit
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
if not isinstance(locale, Locale):
|
|
78
79
|
locale = self._CustomizableBase__locale
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
subdomain = f'{locale.value}.' if locale != Locale.ENGLISH else ''
|
|
81
82
|
delay = 0
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
while True:
|
|
84
85
|
if delay != 0:
|
|
85
86
|
await sleep(delay)
|
|
86
87
|
delay *= 2
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
async with self.__session.get(
|
|
89
90
|
f'https://{subdomain}wttr.in/{quote_plus(location)}?format=j1'
|
|
90
91
|
) as resp:
|
|
91
92
|
try:
|
|
92
|
-
return
|
|
93
|
+
return Forecast(await resp.json(), unit, locale)
|
|
93
94
|
except Exception as e:
|
|
94
95
|
if delay == 4:
|
|
95
96
|
raise e # okay, that's too much requests - just raise the error
|
|
96
97
|
elif delay == 0:
|
|
97
98
|
delay = 0.5
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
async def close(self):
|
|
100
101
|
"""Closes the :class:`Client` object. Nothing will happen if it's already closed."""
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
if not self.__session.closed:
|
|
103
104
|
await self.__session.close()
|
|
104
|
-
|
|
105
|
+
|
|
105
106
|
async def __aenter__(self):
|
|
106
107
|
return self
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
async def __aexit__(self, *_, **__):
|
|
109
110
|
await self.close()
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
from typing import Union
|
|
2
2
|
from re import compile
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
class _Unit:
|
|
6
6
|
__slots__ = (
|
|
7
|
-
'temperature',
|
|
8
|
-
'
|
|
7
|
+
'temperature',
|
|
8
|
+
'velocity',
|
|
9
|
+
'pressure',
|
|
10
|
+
'precipitation',
|
|
11
|
+
'visibility',
|
|
12
|
+
'cm_divisor',
|
|
9
13
|
)
|
|
10
|
-
|
|
14
|
+
|
|
11
15
|
def __init__(
|
|
12
16
|
self,
|
|
13
17
|
temperature: str,
|
|
@@ -23,11 +27,13 @@ class _Unit:
|
|
|
23
27
|
self.precipitation = precipitation
|
|
24
28
|
self.visibility = visibility
|
|
25
29
|
self.cm_divisor = cm_divisor
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
def __repr__(self) -> str:
|
|
28
32
|
return f'<Unit [{self.temperature}, {self.velocity}]>'
|
|
29
33
|
|
|
34
|
+
|
|
30
35
|
METRIC = _Unit('C', 'Kmph', '', 'MM', '', 1)
|
|
31
36
|
IMPERIAL = _Unit('F', 'Miles', 'Inches', 'Inches', 'Miles', 2.54)
|
|
32
37
|
|
|
38
|
+
WIND_DIRECTION_EMOJIS = ['↓', '↙', '←', '↖', '↑', '↗', '→', '↘']
|
|
33
39
|
LATLON_REGEX = compile(r'^Lat (\-?[\d\.]+) and Lon (\-?[\d\.]+)$')
|