python-weather 2.0.4__tar.gz → 2.0.5__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.4/python_weather.egg-info → python_weather-2.0.5}/PKG-INFO +4 -4
- {python_weather-2.0.4 → python_weather-2.0.5}/pyproject.toml +5 -5
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/__init__.py +1 -1
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/base.py +53 -66
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/client.py +2 -1
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/enums.py +5 -8
- python_weather-2.0.5/python_weather/forecast.py +296 -0
- {python_weather-2.0.4 → python_weather-2.0.5/python_weather.egg-info}/PKG-INFO +4 -4
- python_weather-2.0.5/python_weather.egg-info/requires.txt +1 -0
- python_weather-2.0.4/python_weather/forecast.py +0 -350
- python_weather-2.0.4/python_weather.egg-info/requires.txt +0 -1
- {python_weather-2.0.4 → python_weather-2.0.5}/LICENSE +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/MANIFEST.in +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/README.md +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/constants.py +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather/errors.py +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather.egg-info/SOURCES.txt +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather.egg-info/dependency_links.txt +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/python_weather.egg-info/top_level.txt +0 -0
- {python_weather-2.0.4 → python_weather-2.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-weather
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: A free and asynchronous weather API wrapper made in Python, for Python.
|
|
5
5
|
Author: null8626
|
|
6
6
|
License: MIT
|
|
@@ -11,12 +11,11 @@ Project-URL: Donate via GitHub Sponsors, https://github.com/sponsors/null8626
|
|
|
11
11
|
Project-URL: Donate via Ko-fi, https://ko-fi.com/null8626
|
|
12
12
|
Keywords: weather,forecast,weather-api,weather-forecast
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Framework :: aiohttp
|
|
15
|
-
Classifier: Framework :: AsyncIO
|
|
16
14
|
Classifier: Intended Audience :: Education
|
|
17
15
|
Classifier: Intended Audience :: Developers
|
|
18
16
|
Classifier: Intended Audience :: Information Technology
|
|
19
17
|
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
20
19
|
Classifier: Topic :: Education
|
|
21
20
|
Classifier: Topic :: Internet
|
|
22
21
|
Classifier: Topic :: Scientific/Engineering
|
|
@@ -32,10 +31,11 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
32
31
|
Classifier: Programming Language :: Python :: 3.10
|
|
33
32
|
Classifier: Programming Language :: Python :: 3.11
|
|
34
33
|
Classifier: Programming Language :: Python :: 3.12
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
35
35
|
Requires-Python: >=3.8
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
License-File: LICENSE
|
|
38
|
-
Requires-Dist: aiohttp>=3.10.
|
|
38
|
+
Requires-Dist: aiohttp>=3.10.10
|
|
39
39
|
|
|
40
40
|
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![codacy-badge][codacy-url]][codacy-image] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
|
|
41
41
|
|
|
@@ -3,21 +3,20 @@ requires = ["setuptools"]
|
|
|
3
3
|
|
|
4
4
|
[project]
|
|
5
5
|
name = "python-weather"
|
|
6
|
-
version = "2.0.
|
|
6
|
+
version = "2.0.5"
|
|
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" }
|
|
10
10
|
authors = [{ name = "null8626" }]
|
|
11
11
|
keywords = ["weather", "forecast", "weather-api", "weather-forecast"]
|
|
12
|
-
dependencies = ["aiohttp>=3.10.
|
|
12
|
+
dependencies = ["aiohttp>=3.10.10"]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 5 - Production/Stable",
|
|
15
|
-
"Framework :: aiohttp",
|
|
16
|
-
"Framework :: AsyncIO",
|
|
17
15
|
"Intended Audience :: Education",
|
|
18
16
|
"Intended Audience :: Developers",
|
|
19
17
|
"Intended Audience :: Information Technology",
|
|
20
18
|
"Natural Language :: English",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
21
20
|
"Topic :: Education",
|
|
22
21
|
"Topic :: Internet",
|
|
23
22
|
"Topic :: Scientific/Engineering",
|
|
@@ -32,7 +31,8 @@ classifiers = [
|
|
|
32
31
|
"Programming Language :: Python :: 3.9",
|
|
33
32
|
"Programming Language :: Python :: 3.10",
|
|
34
33
|
"Programming Language :: Python :: 3.11",
|
|
35
|
-
"Programming Language :: Python :: 3.12"
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13"
|
|
36
36
|
]
|
|
37
37
|
requires-python = ">=3.8"
|
|
38
38
|
|
|
@@ -79,84 +79,71 @@ class CustomizableBase:
|
|
|
79
79
|
self.__locale = to
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
class BaseForecast
|
|
83
|
-
__slots__: Tuple[str, ...] = (
|
|
82
|
+
class BaseForecast:
|
|
83
|
+
__slots__: Tuple[str, ...] = (
|
|
84
|
+
'ultraviolet',
|
|
85
|
+
'humidity',
|
|
86
|
+
'wind_direction',
|
|
87
|
+
'kind',
|
|
88
|
+
'feels_like',
|
|
89
|
+
'temperature',
|
|
90
|
+
'precipitation',
|
|
91
|
+
'pressure',
|
|
92
|
+
'visibility',
|
|
93
|
+
'wind_speed',
|
|
94
|
+
'description',
|
|
95
|
+
)
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
super().__init__(unit, locale)
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def ultraviolet(self) -> UltraViolet:
|
|
92
|
-
"""The ultra-violet (UV) index."""
|
|
93
|
-
|
|
94
|
-
return UltraViolet._new(int(self.__inner['uvIndex']))
|
|
95
|
-
|
|
96
|
-
@property
|
|
97
|
-
def feels_like(self) -> int:
|
|
98
|
-
"""What it felt like, in celcius or fahrenheit."""
|
|
97
|
+
ultraviolet: UltraViolet
|
|
98
|
+
"""The ultra-violet (UV) index."""
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
humidity: int
|
|
101
|
+
"""The humidity value in percent."""
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"""The humidity value in percent."""
|
|
103
|
+
wind_direction: WindDirection
|
|
104
|
+
"""The wind direction."""
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
kind: Kind
|
|
107
|
+
"""The kind of the forecast."""
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"""The temperature in either celcius or Fahrenheit."""
|
|
109
|
+
feels_like: int
|
|
110
|
+
"""What it felt like, in celcius or fahrenheit."""
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
temperature: int
|
|
113
|
+
"""The temperature in either celcius or Fahrenheit."""
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"""The precipitation in either millimeters or inches."""
|
|
115
|
+
precipitation: float
|
|
116
|
+
"""The precipitation in either millimeters or inches."""
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
pressure: float
|
|
119
|
+
"""The pressure in either pascal or inches."""
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
"""The pressure in either pascal or inches."""
|
|
121
|
+
visibility: int
|
|
122
|
+
"""The visibility distance in either kilometers or miles."""
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
wind_speed: int
|
|
125
|
+
"""The wind speeds in either kilometers/hour or miles/hour."""
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"""The visibility distance in either kilometers or miles."""
|
|
129
|
-
|
|
130
|
-
return int(self.__inner[f'visibility{self._CustomizableBase__unit.visibility}'])
|
|
131
|
-
|
|
132
|
-
@property
|
|
133
|
-
def wind_speed(self) -> int:
|
|
134
|
-
"""The wind speeds in either kilometers/hour or miles/hour."""
|
|
135
|
-
|
|
136
|
-
return int(self.__inner[f'windspeed{self._CustomizableBase__unit.velocity}'])
|
|
137
|
-
|
|
138
|
-
@property
|
|
139
|
-
def wind_direction(self) -> WindDirection:
|
|
140
|
-
"""The wind direction."""
|
|
141
|
-
|
|
142
|
-
return WindDirection._new(
|
|
143
|
-
self.__inner['winddir16Point'], int(self.__inner['winddirDegree'])
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
@property
|
|
147
|
-
def description(self) -> str:
|
|
148
|
-
"""The description regarding the forecast. This can be localized in different languages depending on the localization used."""
|
|
127
|
+
description: str
|
|
128
|
+
"""The description regarding the forecast. This can be localized in different languages depending on the localization used."""
|
|
149
129
|
|
|
130
|
+
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
150
131
|
description = (
|
|
151
|
-
|
|
152
|
-
if
|
|
153
|
-
else
|
|
132
|
+
json['weatherDesc'][0]['value']
|
|
133
|
+
if locale is Locale.ENGLISH
|
|
134
|
+
else json[f'lang_{locale.value}'][0]['value']
|
|
154
135
|
)
|
|
155
136
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
137
|
+
self.ultraviolet = UltraViolet._new(int(json['uvIndex']))
|
|
138
|
+
self.humidity = int(json['humidity'])
|
|
139
|
+
self.wind_direction = WindDirection._new(
|
|
140
|
+
json['winddir16Point'], int(json['winddirDegree'])
|
|
141
|
+
)
|
|
142
|
+
self.kind = Kind(int(json['weatherCode']))
|
|
143
|
+
self.feels_like = int(json[f'FeelsLike{unit.temperature}'])
|
|
144
|
+
self.temperature = int(json[f'temp_{unit.temperature}'])
|
|
145
|
+
self.precipitation = float(json[f'precip{unit.precipitation}'])
|
|
146
|
+
self.pressure = float(json[f'pressure{unit.pressure}'])
|
|
147
|
+
self.visibility = int(json[f'visibility{unit.visibility}'])
|
|
148
|
+
self.wind_speed = int(json[f'windspeed{unit.velocity}'])
|
|
149
|
+
self.description = description.strip()
|
|
@@ -62,7 +62,6 @@ class Client(CustomizableBase):
|
|
|
62
62
|
|
|
63
63
|
self.__own_session = session is None
|
|
64
64
|
self.__session = session or ClientSession(
|
|
65
|
-
raise_for_status=True,
|
|
66
65
|
timeout=ClientTimeout(total=5000.0),
|
|
67
66
|
connector=TCPConnector(verify_ssl=False),
|
|
68
67
|
)
|
|
@@ -112,6 +111,8 @@ class Client(CustomizableBase):
|
|
|
112
111
|
async with self.__session.get(
|
|
113
112
|
f'https://{subdomain}wttr.in/{quote_plus(location)}?format=j1'
|
|
114
113
|
) as resp:
|
|
114
|
+
resp.raise_for_status()
|
|
115
|
+
|
|
115
116
|
return Forecast(await resp.json(), unit, locale)
|
|
116
117
|
except Exception as err:
|
|
117
118
|
if delay == 4:
|
|
@@ -122,7 +122,7 @@ class UltraViolet(BasicEnum, IndexedEnum):
|
|
|
122
122
|
class WindDirection(BasicEnum):
|
|
123
123
|
"""Represents a wind direction."""
|
|
124
124
|
|
|
125
|
-
__slots__: Tuple[str, ...] = ('
|
|
125
|
+
__slots__: Tuple[str, ...] = ('degrees',)
|
|
126
126
|
|
|
127
127
|
NORTH = 'N'
|
|
128
128
|
NORTH_NORTHEAST = 'NNE'
|
|
@@ -141,9 +141,12 @@ class WindDirection(BasicEnum):
|
|
|
141
141
|
NORTHWEST = 'NW'
|
|
142
142
|
NORTH_NORTHWEST = 'NNW'
|
|
143
143
|
|
|
144
|
+
degrees: float
|
|
145
|
+
"""The wind direction's value in degrees."""
|
|
146
|
+
|
|
144
147
|
def _new(value: str, degrees: float) -> 'WindDirection':
|
|
145
148
|
enum = WindDirection(value)
|
|
146
|
-
enum.
|
|
149
|
+
enum.degrees = degrees
|
|
147
150
|
|
|
148
151
|
return enum
|
|
149
152
|
|
|
@@ -186,12 +189,6 @@ class WindDirection(BasicEnum):
|
|
|
186
189
|
def __float__(self) -> float:
|
|
187
190
|
return self.__degrees
|
|
188
191
|
|
|
189
|
-
@property
|
|
190
|
-
def degrees(self) -> int:
|
|
191
|
-
"""The wind direction's value in degrees."""
|
|
192
|
-
|
|
193
|
-
return self.__degrees
|
|
194
|
-
|
|
195
192
|
@property
|
|
196
193
|
def emoji(self) -> str:
|
|
197
194
|
"""The emoji representing this enum."""
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The MIT License (MIT)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2021-2024 null8626
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import Iterable, Optional, Tuple, List
|
|
26
|
+
from datetime import datetime, date, time
|
|
27
|
+
|
|
28
|
+
from .base import BaseForecast
|
|
29
|
+
from .enums import Phase, Locale, HeatIndex
|
|
30
|
+
from .constants import _Unit, LATLON_REGEX
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class HourlyForecast(BaseForecast):
|
|
34
|
+
"""Represents a weather forecast of a specific hour."""
|
|
35
|
+
|
|
36
|
+
__slots__: Tuple[str, ...] = (
|
|
37
|
+
'chances_of_fog',
|
|
38
|
+
'chances_of_frost',
|
|
39
|
+
'chances_of_high_temperature',
|
|
40
|
+
'chances_of_overcast',
|
|
41
|
+
'chances_of_rain',
|
|
42
|
+
'chances_of_remaining_dry',
|
|
43
|
+
'chances_of_snow',
|
|
44
|
+
'chances_of_sunshine',
|
|
45
|
+
'chances_of_thunder',
|
|
46
|
+
'chances_of_windy',
|
|
47
|
+
'cloud_cover',
|
|
48
|
+
'time',
|
|
49
|
+
'dew_point',
|
|
50
|
+
'heat_index',
|
|
51
|
+
'wind_chill',
|
|
52
|
+
'wind_gust',
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
chances_of_fog: int
|
|
56
|
+
"""Chances of a fog in percent."""
|
|
57
|
+
|
|
58
|
+
chances_of_frost: int
|
|
59
|
+
"""Chances of a frost in percent."""
|
|
60
|
+
|
|
61
|
+
chances_of_high_temperature: int
|
|
62
|
+
"""Chances of a high temperature in percent."""
|
|
63
|
+
|
|
64
|
+
chances_of_overcast: int
|
|
65
|
+
"""Chances of an overcast in percent."""
|
|
66
|
+
|
|
67
|
+
chances_of_rain: int
|
|
68
|
+
"""Chances of a rain in percent."""
|
|
69
|
+
|
|
70
|
+
chances_of_remaining_dry: int
|
|
71
|
+
"""Chances of remaining dry in percent."""
|
|
72
|
+
|
|
73
|
+
chances_of_snow: int
|
|
74
|
+
"""Chances of a snow in percent."""
|
|
75
|
+
|
|
76
|
+
chances_of_sunshine: int
|
|
77
|
+
"""Chances of a sunshine in percent."""
|
|
78
|
+
|
|
79
|
+
chances_of_thunder: int
|
|
80
|
+
"""Chances of a thunder in percent."""
|
|
81
|
+
|
|
82
|
+
chances_of_windy: int
|
|
83
|
+
"""Chances of windy in percent."""
|
|
84
|
+
|
|
85
|
+
cloud_cover: int
|
|
86
|
+
"""The cloud cover value in percent."""
|
|
87
|
+
|
|
88
|
+
time: 'time'
|
|
89
|
+
"""The local time in hours and minutes."""
|
|
90
|
+
|
|
91
|
+
dew_point: int
|
|
92
|
+
"""The dew point in either celcius or fahrenheit."""
|
|
93
|
+
|
|
94
|
+
heat_index: HeatIndex
|
|
95
|
+
"""The heat index in either celcius or fahrenheit."""
|
|
96
|
+
|
|
97
|
+
wind_chill: int
|
|
98
|
+
"""The wind chill value in either celcius or fahrenheit."""
|
|
99
|
+
|
|
100
|
+
wind_gust: int
|
|
101
|
+
"""The wind gust value in either kilometers/hour or miles/hour."""
|
|
102
|
+
|
|
103
|
+
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
104
|
+
# for inheritance purposes
|
|
105
|
+
if 'temp_C' not in json:
|
|
106
|
+
json['temp_C'] = json.pop('tempC')
|
|
107
|
+
if 'temp_F' not in json:
|
|
108
|
+
json['temp_F'] = json.pop('tempF')
|
|
109
|
+
|
|
110
|
+
celcius_index = int(json['HeatIndexC'])
|
|
111
|
+
t = json['time']
|
|
112
|
+
|
|
113
|
+
self.chances_of_fog = int(json['chanceoffog'])
|
|
114
|
+
self.chances_of_frost = int(json['chanceoffrost'])
|
|
115
|
+
self.chances_of_high_temperature = int(json['chanceofhightemp'])
|
|
116
|
+
self.chances_of_overcast = int(json['chanceofovercast'])
|
|
117
|
+
self.chances_of_rain = int(json['chanceofrain'])
|
|
118
|
+
self.chances_of_remaining_dry = int(json['chanceofremdry'])
|
|
119
|
+
self.chances_of_snow = int(json['chanceofsnow'])
|
|
120
|
+
self.chances_of_sunshine = int(json['chanceofsunshine'])
|
|
121
|
+
self.chances_of_thunder = int(json['chanceofthunder'])
|
|
122
|
+
self.chances_of_windy = int(json['chanceofwindy'])
|
|
123
|
+
self.cloud_cover = int(json['cloudcover'])
|
|
124
|
+
self.time = time() if len(t) < 3 else datetime.strptime(t, '%H%M').time()
|
|
125
|
+
self.dew_point = int(json[f'DewPoint{unit.temperature}'])
|
|
126
|
+
self.heat_index = HeatIndex._new(
|
|
127
|
+
celcius_index,
|
|
128
|
+
int(json[f'HeatIndex{unit.temperature}']),
|
|
129
|
+
)
|
|
130
|
+
self.wind_chill = int(json[f'WindChill{unit.temperature}'])
|
|
131
|
+
self.wind_gust = int(json[f'WindGust{unit.velocity}'])
|
|
132
|
+
|
|
133
|
+
super().__init__(json, unit, locale)
|
|
134
|
+
|
|
135
|
+
def __repr__(self) -> str:
|
|
136
|
+
return f'<{__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DailyForecast:
|
|
140
|
+
__slots__: Tuple[str, ...] = (
|
|
141
|
+
'moon_illumination',
|
|
142
|
+
'moon_phase',
|
|
143
|
+
'moonrise',
|
|
144
|
+
'moonset',
|
|
145
|
+
'sunrise',
|
|
146
|
+
'sunset',
|
|
147
|
+
'date',
|
|
148
|
+
'sunlight',
|
|
149
|
+
'lowest_temperature',
|
|
150
|
+
'highest_temperature',
|
|
151
|
+
'temperature',
|
|
152
|
+
'snowfall',
|
|
153
|
+
'hourly_forecasts',
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
moon_illumination: int
|
|
157
|
+
"""The percentage of the moon illuminated."""
|
|
158
|
+
|
|
159
|
+
moon_phase: Phase
|
|
160
|
+
"""The moon's phase."""
|
|
161
|
+
|
|
162
|
+
moonrise: Optional[time]
|
|
163
|
+
"""The local time when the moon rises. This can be ``None``."""
|
|
164
|
+
|
|
165
|
+
moonset: Optional[time]
|
|
166
|
+
"""The local time when the moon sets. This can be ``None``."""
|
|
167
|
+
|
|
168
|
+
sunrise: Optional[time]
|
|
169
|
+
"""The local time when the sun rises. This can be ``None``."""
|
|
170
|
+
|
|
171
|
+
sunset: Optional[time]
|
|
172
|
+
"""The local time when the sun sets. This can be ``None``."""
|
|
173
|
+
|
|
174
|
+
date: 'date'
|
|
175
|
+
"""The local date of this forecast."""
|
|
176
|
+
|
|
177
|
+
sunlight: float
|
|
178
|
+
"""Hours of sunlight."""
|
|
179
|
+
|
|
180
|
+
lowest_temperature: int
|
|
181
|
+
"""The lowest temperature in either celcius or fahrenheit."""
|
|
182
|
+
|
|
183
|
+
highest_temperature: int
|
|
184
|
+
"""The highest temperature in either celcius or fahrenheit."""
|
|
185
|
+
|
|
186
|
+
temperature: int
|
|
187
|
+
"""The average temperature in either celcius or fahrenheit."""
|
|
188
|
+
|
|
189
|
+
snowfall: float
|
|
190
|
+
"""Total snowfall in either centimeters or inches."""
|
|
191
|
+
|
|
192
|
+
hourly_forecasts: List[HourlyForecast]
|
|
193
|
+
"""The hourly forecasts of this day."""
|
|
194
|
+
|
|
195
|
+
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
196
|
+
astronomy = json['astronomy'][0]
|
|
197
|
+
|
|
198
|
+
self.moon_illumination = int(astronomy['moon_illumination'])
|
|
199
|
+
self.moon_phase = Phase(astronomy['moon_phase'])
|
|
200
|
+
self.moonrise = __class__.__parse_time(astronomy['moonrise'])
|
|
201
|
+
self.moonset = __class__.__parse_time(astronomy['moonset'])
|
|
202
|
+
self.sunrise = __class__.__parse_time(astronomy['sunrise'])
|
|
203
|
+
self.sunset = __class__.__parse_time(astronomy['sunset'])
|
|
204
|
+
self.date = datetime.strptime(json['date'], '%Y-%m-%d').date()
|
|
205
|
+
self.sunlight = float(json['sunHour'])
|
|
206
|
+
self.lowest_temperature = int(json[f'mintemp{unit.temperature}'])
|
|
207
|
+
self.highest_temperature = int(json[f'maxtemp{unit.temperature}'])
|
|
208
|
+
self.temperature = int(json[f'avgtemp{unit.temperature}'])
|
|
209
|
+
self.snowfall = float(json['totalSnow_cm']) / unit.cm_divisor
|
|
210
|
+
self.hourly_forecasts = [
|
|
211
|
+
HourlyForecast(elem, unit, locale) for elem in json['hourly']
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def __parse_time(timestamp: str) -> Optional[time]:
|
|
216
|
+
try:
|
|
217
|
+
return datetime.strptime(timestamp, '%I:%M %p').time()
|
|
218
|
+
except ValueError:
|
|
219
|
+
...
|
|
220
|
+
|
|
221
|
+
def __repr__(self) -> str:
|
|
222
|
+
return f'<{__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
|
|
223
|
+
|
|
224
|
+
def __len__(self) -> int:
|
|
225
|
+
return len(self.hourly_forecasts)
|
|
226
|
+
|
|
227
|
+
def __iter__(self) -> Iterable[HourlyForecast]:
|
|
228
|
+
return iter(self.hourly_forecasts)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class Forecast(BaseForecast):
|
|
232
|
+
"""Represents today's weather forecast, alongside daily and hourly weather forecasts."""
|
|
233
|
+
|
|
234
|
+
__slots__: Tuple[str, ...] = (
|
|
235
|
+
'local_population',
|
|
236
|
+
'region',
|
|
237
|
+
'location',
|
|
238
|
+
'country',
|
|
239
|
+
'datetime',
|
|
240
|
+
'coordinates',
|
|
241
|
+
'daily_forecasts',
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
local_population: int
|
|
245
|
+
"""The local population count."""
|
|
246
|
+
|
|
247
|
+
region: str
|
|
248
|
+
"""The local region's name."""
|
|
249
|
+
|
|
250
|
+
location: str
|
|
251
|
+
"""The location's name."""
|
|
252
|
+
|
|
253
|
+
country: str
|
|
254
|
+
"""The local country's name."""
|
|
255
|
+
|
|
256
|
+
datetime: 'datetime'
|
|
257
|
+
"""The local date and time of this weather forecast."""
|
|
258
|
+
|
|
259
|
+
coordinates: Tuple[float, float]
|
|
260
|
+
"""A tuple of this forecast's latitude and longitude."""
|
|
261
|
+
|
|
262
|
+
daily_forecasts: List[DailyForecast]
|
|
263
|
+
"""Daily weather forecasts in this location."""
|
|
264
|
+
|
|
265
|
+
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
266
|
+
current = json['current_condition'][0]
|
|
267
|
+
nearest = json['nearest_area'][0]
|
|
268
|
+
|
|
269
|
+
self.local_population = int(nearest['population'])
|
|
270
|
+
self.region = nearest['region'][0]['value']
|
|
271
|
+
self.location = nearest['areaName'][0]['value']
|
|
272
|
+
self.country = nearest['country'][0]['value']
|
|
273
|
+
self.datetime = datetime.strptime(current['localObsDateTime'], '%Y-%m-%d %I:%M %p')
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
req = next(filter(lambda x: x['type'] == 'LatLon', json['request']))
|
|
277
|
+
match = LATLON_REGEX.match(req['query'])
|
|
278
|
+
|
|
279
|
+
self.coordinates = (float(match[1]), float(match[2]))
|
|
280
|
+
except:
|
|
281
|
+
self.coordinates = (float(nearest['latitude']), float(nearest['longitude']))
|
|
282
|
+
|
|
283
|
+
self.daily_forecasts = [
|
|
284
|
+
DailyForecast(elem, unit, locale) for elem in json['weather']
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
super().__init__(current, unit, locale)
|
|
288
|
+
|
|
289
|
+
def __repr__(self) -> str:
|
|
290
|
+
return f'<{__class__.__name__} location={self.location!r} datetime={self.datetime!r} temperature={self.temperature!r}>'
|
|
291
|
+
|
|
292
|
+
def __len__(self) -> int:
|
|
293
|
+
return len(self.daily_forecasts)
|
|
294
|
+
|
|
295
|
+
def __iter__(self) -> Iterable[DailyForecast]:
|
|
296
|
+
return iter(self.daily_forecasts)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-weather
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: A free and asynchronous weather API wrapper made in Python, for Python.
|
|
5
5
|
Author: null8626
|
|
6
6
|
License: MIT
|
|
@@ -11,12 +11,11 @@ Project-URL: Donate via GitHub Sponsors, https://github.com/sponsors/null8626
|
|
|
11
11
|
Project-URL: Donate via Ko-fi, https://ko-fi.com/null8626
|
|
12
12
|
Keywords: weather,forecast,weather-api,weather-forecast
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Framework :: aiohttp
|
|
15
|
-
Classifier: Framework :: AsyncIO
|
|
16
14
|
Classifier: Intended Audience :: Education
|
|
17
15
|
Classifier: Intended Audience :: Developers
|
|
18
16
|
Classifier: Intended Audience :: Information Technology
|
|
19
17
|
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
20
19
|
Classifier: Topic :: Education
|
|
21
20
|
Classifier: Topic :: Internet
|
|
22
21
|
Classifier: Topic :: Scientific/Engineering
|
|
@@ -32,10 +31,11 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
32
31
|
Classifier: Programming Language :: Python :: 3.10
|
|
33
32
|
Classifier: Programming Language :: Python :: 3.11
|
|
34
33
|
Classifier: Programming Language :: Python :: 3.12
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
35
35
|
Requires-Python: >=3.8
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
License-File: LICENSE
|
|
38
|
-
Requires-Dist: aiohttp>=3.10.
|
|
38
|
+
Requires-Dist: aiohttp>=3.10.10
|
|
39
39
|
|
|
40
40
|
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![codacy-badge][codacy-url]][codacy-image] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
|
|
41
41
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
aiohttp>=3.10.10
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
The MIT License (MIT)
|
|
3
|
-
|
|
4
|
-
Copyright (c) 2021-2024 null8626
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
from typing import Iterable, Optional, Tuple, List
|
|
26
|
-
from datetime import datetime, date, time
|
|
27
|
-
|
|
28
|
-
from .base import BaseForecast, CustomizableBase
|
|
29
|
-
from .enums import Phase, Locale, HeatIndex
|
|
30
|
-
from .constants import _Unit, LATLON_REGEX
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class HourlyForecast(BaseForecast):
|
|
34
|
-
"""Represents a weather forecast of a specific hour."""
|
|
35
|
-
|
|
36
|
-
__slots__: Tuple[str, ...] = ()
|
|
37
|
-
|
|
38
|
-
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
39
|
-
# for inheritance purposes
|
|
40
|
-
if 'temp_C' not in json:
|
|
41
|
-
json['temp_C'] = json.pop('tempC')
|
|
42
|
-
if 'temp_F' not in json:
|
|
43
|
-
json['temp_F'] = json.pop('tempF')
|
|
44
|
-
|
|
45
|
-
super().__init__(json, unit, locale)
|
|
46
|
-
|
|
47
|
-
def __repr__(self) -> str:
|
|
48
|
-
return f'<{__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def dew_point(self) -> int:
|
|
52
|
-
"""The dew point in either celcius or fahrenheit."""
|
|
53
|
-
|
|
54
|
-
return int(
|
|
55
|
-
self._BaseForecast__inner[f'DewPoint{self._CustomizableBase__unit.temperature}']
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def heat_index(self) -> HeatIndex:
|
|
60
|
-
"""The heat index in either celcius or fahrenheit."""
|
|
61
|
-
|
|
62
|
-
celcius_index = int(self._BaseForecast__inner['HeatIndexC'])
|
|
63
|
-
|
|
64
|
-
return HeatIndex._new(
|
|
65
|
-
celcius_index,
|
|
66
|
-
int(
|
|
67
|
-
self._BaseForecast__inner[
|
|
68
|
-
f'HeatIndex{self._CustomizableBase__unit.temperature}'
|
|
69
|
-
]
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def wind_chill(self) -> int:
|
|
75
|
-
"""The wind chill value in either celcius or fahrenheit."""
|
|
76
|
-
|
|
77
|
-
return int(
|
|
78
|
-
self._BaseForecast__inner[f'WindChill{self._CustomizableBase__unit.temperature}']
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
@property
|
|
82
|
-
def wind_gust(self) -> int:
|
|
83
|
-
"""The wind gust value in either kilometers/hour or miles/hour."""
|
|
84
|
-
|
|
85
|
-
return int(
|
|
86
|
-
self._BaseForecast__inner[f'WindGust{self._CustomizableBase__unit.velocity}']
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def chances_of_fog(self) -> int:
|
|
91
|
-
"""Chances of a fog in percent."""
|
|
92
|
-
|
|
93
|
-
return int(self._BaseForecast__inner['chanceoffog'])
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def chances_of_frost(self) -> int:
|
|
97
|
-
"""Chances of a frost in percent."""
|
|
98
|
-
|
|
99
|
-
return int(self._BaseForecast__inner['chanceoffrost'])
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
def chances_of_high_temperature(self) -> int:
|
|
103
|
-
"""Chances of a high temperature in percent."""
|
|
104
|
-
|
|
105
|
-
return int(self._BaseForecast__inner['chanceofhightemp'])
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def chances_of_overcast(self) -> int:
|
|
109
|
-
"""Chances of an overcast in percent."""
|
|
110
|
-
|
|
111
|
-
return int(self._BaseForecast__inner['chanceofovercast'])
|
|
112
|
-
|
|
113
|
-
@property
|
|
114
|
-
def chances_of_rain(self) -> int:
|
|
115
|
-
"""Chances of a rain in percent."""
|
|
116
|
-
|
|
117
|
-
return int(self._BaseForecast__inner['chanceofrain'])
|
|
118
|
-
|
|
119
|
-
@property
|
|
120
|
-
def chances_of_remaining_dry(self) -> int:
|
|
121
|
-
"""Chances of remaining dry in percent."""
|
|
122
|
-
|
|
123
|
-
return int(self._BaseForecast__inner['chanceofremdry'])
|
|
124
|
-
|
|
125
|
-
@property
|
|
126
|
-
def chances_of_snow(self) -> int:
|
|
127
|
-
"""Chances of a snow in percent."""
|
|
128
|
-
|
|
129
|
-
return int(self._BaseForecast__inner['chanceofsnow'])
|
|
130
|
-
|
|
131
|
-
@property
|
|
132
|
-
def chances_of_sunshine(self) -> int:
|
|
133
|
-
"""Chances of a sunshine in percent."""
|
|
134
|
-
|
|
135
|
-
return int(self._BaseForecast__inner['chanceofsunshine'])
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def chances_of_thunder(self) -> int:
|
|
139
|
-
"""Chances of a thunder in percent."""
|
|
140
|
-
|
|
141
|
-
return int(self._BaseForecast__inner['chanceofthunder'])
|
|
142
|
-
|
|
143
|
-
@property
|
|
144
|
-
def chances_of_windy(self) -> int:
|
|
145
|
-
"""Chances of windy in percent."""
|
|
146
|
-
|
|
147
|
-
return int(self._BaseForecast__inner['chanceofwindy'])
|
|
148
|
-
|
|
149
|
-
@property
|
|
150
|
-
def cloud_cover(self) -> int:
|
|
151
|
-
"""The cloud cover value in percent."""
|
|
152
|
-
|
|
153
|
-
return int(self._BaseForecast__inner['cloudcover'])
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def time(self) -> time:
|
|
157
|
-
"""The local time in hours and minutes."""
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
time()
|
|
161
|
-
if len(self._BaseForecast__inner['time']) < 3
|
|
162
|
-
else datetime.strptime(self._BaseForecast__inner['time'], '%H%M').time()
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
class DailyForecast(CustomizableBase):
|
|
167
|
-
__slots__: Tuple[str, ...] = ('__inner', '__astronomy')
|
|
168
|
-
|
|
169
|
-
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
170
|
-
self.__astronomy = json.pop('astronomy')[0]
|
|
171
|
-
self.__inner = json
|
|
172
|
-
|
|
173
|
-
super().__init__(unit, locale)
|
|
174
|
-
|
|
175
|
-
def __repr__(self) -> str:
|
|
176
|
-
return f'<{__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
|
|
177
|
-
|
|
178
|
-
def __iter__(self) -> Iterable[HourlyForecast]:
|
|
179
|
-
return self.hourly_forecasts
|
|
180
|
-
|
|
181
|
-
def __list__(self) -> List[HourlyForecast]:
|
|
182
|
-
return list(iter(self))
|
|
183
|
-
|
|
184
|
-
@property
|
|
185
|
-
def moon_illumination(self) -> int:
|
|
186
|
-
"""The percentage of the moon illuminated."""
|
|
187
|
-
|
|
188
|
-
return int(self.__astronomy['moon_illumination'])
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
def moon_phase(self) -> Phase:
|
|
192
|
-
"""The moon's phase."""
|
|
193
|
-
|
|
194
|
-
return Phase(self.__astronomy['moon_phase'])
|
|
195
|
-
|
|
196
|
-
@property
|
|
197
|
-
def moonrise(self) -> Optional[time]:
|
|
198
|
-
"""The local time when the moon rises. This can be ``None``."""
|
|
199
|
-
|
|
200
|
-
try:
|
|
201
|
-
return datetime.strptime(self.__astronomy['moonrise'], '%I:%M %p').time()
|
|
202
|
-
except ValueError:
|
|
203
|
-
...
|
|
204
|
-
|
|
205
|
-
@property
|
|
206
|
-
def moonset(self) -> Optional[time]:
|
|
207
|
-
"""The local time when the moon sets. This can be ``None``."""
|
|
208
|
-
|
|
209
|
-
try:
|
|
210
|
-
return datetime.strptime(self.__astronomy['moonset'], '%I:%M %p').time()
|
|
211
|
-
except ValueError:
|
|
212
|
-
...
|
|
213
|
-
|
|
214
|
-
@property
|
|
215
|
-
def sunrise(self) -> Optional[time]:
|
|
216
|
-
"""The local time when the sun rises. This can be ``None``."""
|
|
217
|
-
|
|
218
|
-
try:
|
|
219
|
-
return datetime.strptime(self.__astronomy['sunrise'], '%I:%M %p').time()
|
|
220
|
-
except ValueError:
|
|
221
|
-
...
|
|
222
|
-
|
|
223
|
-
@property
|
|
224
|
-
def sunset(self) -> Optional[time]:
|
|
225
|
-
"""The local time when the sun sets. This can be ``None``."""
|
|
226
|
-
|
|
227
|
-
try:
|
|
228
|
-
return datetime.strptime(self.__astronomy['sunset'], '%I:%M %p').time()
|
|
229
|
-
except ValueError:
|
|
230
|
-
...
|
|
231
|
-
|
|
232
|
-
@property
|
|
233
|
-
def date(self) -> date:
|
|
234
|
-
"""The local date of this forecast."""
|
|
235
|
-
|
|
236
|
-
return datetime.strptime(self.__inner['date'], '%Y-%m-%d').date()
|
|
237
|
-
|
|
238
|
-
@property
|
|
239
|
-
def lowest_temperature(self) -> int:
|
|
240
|
-
"""The lowest temperature in either celcius or fahrenheit."""
|
|
241
|
-
|
|
242
|
-
return int(self.__inner[f'mintemp{self._CustomizableBase__unit.temperature}'])
|
|
243
|
-
|
|
244
|
-
@property
|
|
245
|
-
def highest_temperature(self) -> int:
|
|
246
|
-
"""The highest temperature in either celcius or fahrenheit."""
|
|
247
|
-
|
|
248
|
-
return int(self.__inner[f'maxtemp{self._CustomizableBase__unit.temperature}'])
|
|
249
|
-
|
|
250
|
-
@property
|
|
251
|
-
def temperature(self) -> int:
|
|
252
|
-
"""The average temperature in either celcius or fahrenheit."""
|
|
253
|
-
|
|
254
|
-
return int(self.__inner[f'avgtemp{self._CustomizableBase__unit.temperature}'])
|
|
255
|
-
|
|
256
|
-
@property
|
|
257
|
-
def sunlight(self) -> float:
|
|
258
|
-
"""Hours of sunlight."""
|
|
259
|
-
|
|
260
|
-
return float(self.__inner['sunHour'])
|
|
261
|
-
|
|
262
|
-
@property
|
|
263
|
-
def snowfall(self) -> float:
|
|
264
|
-
"""Total snowfall in either centimeters or inches."""
|
|
265
|
-
|
|
266
|
-
return float(self.__inner['totalSnow_cm']) / self._CustomizableBase__unit.cm_divisor
|
|
267
|
-
|
|
268
|
-
@property
|
|
269
|
-
def hourly_forecasts(self) -> Iterable[HourlyForecast]:
|
|
270
|
-
"""The hourly forecasts of this day."""
|
|
271
|
-
|
|
272
|
-
return (
|
|
273
|
-
HourlyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
|
|
274
|
-
for elem in self.__inner['hourly']
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
class Forecast(BaseForecast):
|
|
279
|
-
"""Represents today's weather forecast, alongside daily and hourly weather forecasts."""
|
|
280
|
-
|
|
281
|
-
__slots__: Tuple[str, ...] = ('__inner', '__nearest')
|
|
282
|
-
|
|
283
|
-
def __init__(self, json: dict, unit: _Unit, locale: Locale):
|
|
284
|
-
current = json['current_condition'][0]
|
|
285
|
-
self.__nearest = json.pop('nearest_area')[0]
|
|
286
|
-
self.__inner = json
|
|
287
|
-
|
|
288
|
-
super().__init__(current, unit, locale)
|
|
289
|
-
|
|
290
|
-
def __repr__(self) -> str:
|
|
291
|
-
return f'<{__class__.__name__} location={self.location!r} datetime={self.datetime!r} temperature={self.temperature!r}>'
|
|
292
|
-
|
|
293
|
-
def __iter__(self) -> Iterable[DailyForecast]:
|
|
294
|
-
return self.daily_forecasts
|
|
295
|
-
|
|
296
|
-
def __list__(self) -> List[DailyForecast]:
|
|
297
|
-
return list(iter(self))
|
|
298
|
-
|
|
299
|
-
@property
|
|
300
|
-
def local_population(self) -> int:
|
|
301
|
-
"""The local population count."""
|
|
302
|
-
|
|
303
|
-
return int(self.__nearest['population'])
|
|
304
|
-
|
|
305
|
-
@property
|
|
306
|
-
def region(self) -> str:
|
|
307
|
-
"""The local region's name."""
|
|
308
|
-
|
|
309
|
-
return self.__nearest['region'][0]['value']
|
|
310
|
-
|
|
311
|
-
@property
|
|
312
|
-
def location(self) -> str:
|
|
313
|
-
"""The location's name."""
|
|
314
|
-
|
|
315
|
-
return self.__nearest['areaName'][0]['value']
|
|
316
|
-
|
|
317
|
-
@property
|
|
318
|
-
def country(self) -> str:
|
|
319
|
-
"""The local country's name."""
|
|
320
|
-
|
|
321
|
-
return self.__nearest['country'][0]['value']
|
|
322
|
-
|
|
323
|
-
@property
|
|
324
|
-
def datetime(self) -> datetime:
|
|
325
|
-
"""The local date and time of this weather forecast."""
|
|
326
|
-
|
|
327
|
-
return datetime.strptime(
|
|
328
|
-
self._BaseForecast__inner['localObsDateTime'], '%Y-%m-%d %I:%M %p'
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
@property
|
|
332
|
-
def daily_forecasts(self) -> Iterable[DailyForecast]:
|
|
333
|
-
"""Daily weather forecasts in this location."""
|
|
334
|
-
|
|
335
|
-
return (
|
|
336
|
-
DailyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
|
|
337
|
-
for elem in self.__inner['weather']
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
@property
|
|
341
|
-
def coordinates(self) -> Tuple[float, float]:
|
|
342
|
-
"""A tuple of this forecast's latitude and longitude."""
|
|
343
|
-
|
|
344
|
-
try:
|
|
345
|
-
req = next(filter(lambda x: x['type'] == 'LatLon', self.__inner['request']))
|
|
346
|
-
match = LATLON_REGEX.match(req['query'])
|
|
347
|
-
|
|
348
|
-
return float(match[1]), float(match[2])
|
|
349
|
-
except:
|
|
350
|
-
return float(self.__nearest['latitude']), float(self.__nearest['longitude'])
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
aiohttp>=3.10.8
|
|
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
|