python-weather 2.0.0__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.
@@ -1,9 +1,10 @@
1
+ prune .ruff_cache
1
2
  prune .github
2
3
  prune docs
3
4
  exclude .gitattributes
4
5
  exclude .gitignore
5
6
  exclude .readthedocs.yml
6
- exclude .style.yapf
7
+ exclude ruff.toml
7
8
  exclude LICENSE
8
9
  exclude changelog.yml
9
10
  exclude test.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-weather
3
- Version: 2.0.0
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
@@ -3,7 +3,7 @@ requires = ["setuptools"]
3
3
 
4
4
  [project]
5
5
  name = "python-weather"
6
- version = "2.0.0"
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" }
@@ -1,14 +1,18 @@
1
- """
2
- python-weather
3
- """
4
-
5
1
  from .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
6
2
  from .constants import METRIC, IMPERIAL
7
3
  from .client import Client
8
4
  from .errors import Error
9
5
 
10
- __version__ = '2.0.0'
6
+ __version__ = '2.0.1'
11
7
  __all__ = (
12
- 'METRIC', 'IMPERIAL', 'Client', 'Error', 'HeatIndex', 'Kind', 'Locale',
13
- 'Phase', 'UltraViolet', 'WindDirection'
8
+ 'METRIC',
9
+ 'IMPERIAL',
10
+ 'Client',
11
+ 'Error',
12
+ 'HeatIndex',
13
+ 'Kind',
14
+ 'Locale',
15
+ 'Phase',
16
+ 'UltraViolet',
17
+ 'WindDirection',
14
18
  )
@@ -1,140 +1,137 @@
1
- from enum import auto, Enum
1
+ from enum import auto
2
2
 
3
+ from .errors import Error
3
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
66
  def ultraviolet(self) -> UltraViolet:
64
67
  """The ultra-violet (UV) index."""
65
-
68
+
66
69
  return UltraViolet._new(int(self.__inner['uvIndex']))
67
-
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
- self.__inner[f'FeelsLike{self._CustomizableBase__unit.temperature}']
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
85
  """The temperature in either Celcius or Fahrenheit."""
85
-
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
- self.__inner[f'precip{self._CustomizableBase__unit.precipitation}']
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
- self.__inner[f'pressure{self._CustomizableBase__unit.pressure}']
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
- self.__inner[f'visibility{self._CustomizableBase__unit.visibility}']
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
- self.__inner[f'windspeed{self._CustomizableBase__unit.velocity}']
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
- description = self.__inner['weatherDesc'][0]['value'] if self._CustomizableBase__locale == Locale.ENGLISH else self.__inner[f'lang_{self._CustomizableBase__locale.value}'][0]['value'] # yapf: disable
133
-
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
+
134
131
  return description.strip()
135
-
132
+
136
133
  @property
137
134
  def kind(self) -> Kind:
138
135
  """The kind of the forecast."""
139
-
136
+
140
137
  return Kind(int(self.__inner['weatherCode']))
@@ -10,81 +10,82 @@ 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
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
+ locale: Optional[Locale] = None,
52
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
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:
@@ -95,15 +96,15 @@ class Client(CustomizableBase):
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
- from enum import auto
3
+
4
4
 
5
5
  class _Unit:
6
6
  __slots__ = (
7
- 'temperature', 'velocity', 'pressure', 'precipitation', 'visibility',
8
- 'cm_divisor'
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,10 +27,11 @@ 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
 
@@ -1,69 +1,71 @@
1
- from enum import Enum, auto
1
+ from enum import Enum
2
2
  from typing import Union
3
3
 
4
4
  from .constants import WIND_DIRECTION_EMOJIS
5
- from .errors import Error
5
+
6
6
 
7
7
  class BasicEnum(Enum):
8
8
  __slots__ = ()
9
-
9
+
10
10
  def __repr__(self) -> str:
11
11
  return f'{self.__class__.__name__}.{self.name}'
12
-
12
+
13
13
  def __str__(self) -> str:
14
14
  return self.name.replace('_', ' ').title()
15
15
 
16
+
16
17
  class IndexedEnum(Enum):
17
18
  __slots__ = ('__index',)
18
-
19
- def __lt__(self, other: Union["Self", int, float]) -> bool:
19
+
20
+ def __lt__(self, other: Union['IndexedEnum', int, float]) -> bool:
20
21
  if isinstance(other, self.__class__):
21
22
  return self.__index < other.index
22
23
  else:
23
24
  return self.__index < other
24
-
25
- def __eq__(self, other: Union["Self", int, float]) -> bool:
25
+
26
+ def __eq__(self, other: Union['IndexedEnum', int, float]) -> bool:
26
27
  if isinstance(other, self.__class__):
27
28
  return self.__index == other.index
28
29
  else:
29
30
  return self.__index == other
30
-
31
- def __gt__(self, other: Union["Self", int, float]) -> bool:
31
+
32
+ def __gt__(self, other: Union['IndexedEnum', int, float]) -> bool:
32
33
  if isinstance(other, self.__class__):
33
34
  return self.__index > other.index
34
35
  else:
35
36
  return self.__index > other
36
-
37
+
37
38
  def __hash__(self) -> int:
38
39
  return self.__index
39
-
40
+
40
41
  def __int__(self) -> int:
41
42
  return self.__index
42
-
43
+
43
44
  @property
44
45
  def index(self) -> int:
45
46
  """The index value."""
46
-
47
+
47
48
  return self.__index
48
-
49
+
49
50
  @index.setter
50
51
  def index(self, new_index: int) -> int:
51
52
  self.__index = new_index
52
53
 
54
+
53
55
  class HeatIndex(IndexedEnum):
54
56
  """Represents a heat index."""
55
-
57
+
56
58
  CAUTION = None
57
59
  EXTREME_CAUTION = None
58
60
  DANGER = None
59
61
  EXTREME_DANGER = None
60
-
62
+
61
63
  def _new(celcius_index: int, true_index: int):
62
64
  enum = HeatIndex(celcius_index)
63
65
  enum.index = true_index
64
-
66
+
65
67
  return enum
66
-
68
+
67
69
  @classmethod
68
70
  def _missing_(self, celcius_index: int):
69
71
  if celcius_index <= 32:
@@ -75,21 +77,22 @@ class HeatIndex(IndexedEnum):
75
77
  else:
76
78
  return self.EXTREME_DANGER
77
79
 
80
+
78
81
  class UltraViolet(BasicEnum, IndexedEnum):
79
82
  """Represents ultra-violet (UV) index."""
80
-
83
+
81
84
  LOW = None
82
85
  MODERATE = None
83
86
  HIGH = None
84
87
  VERY_HIGH = None
85
88
  EXTREME = None
86
-
89
+
87
90
  def _new(index: int):
88
91
  enum = UltraViolet(index)
89
92
  enum.index = index
90
-
93
+
91
94
  return enum
92
-
95
+
93
96
  @classmethod
94
97
  def _missing_(self, index: int):
95
98
  if index <= 2:
@@ -103,38 +106,39 @@ class UltraViolet(BasicEnum, IndexedEnum):
103
106
  else:
104
107
  return self.EXTREME
105
108
 
109
+
106
110
  class WindDirection(BasicEnum):
107
111
  """Represents a wind direction."""
108
-
112
+
109
113
  __slots__ = ('__degrees',)
110
-
111
- NORTH = "N"
112
- NORTH_NORTHEAST = "NNE"
113
- NORTHEAST = "NE"
114
- EAST_NORTHEAST = "ENE"
115
- EAST = "E"
116
- EAST_SOUTHEAST = "ESE"
117
- SOUTHEAST = "SE"
118
- SOUTH_SOUTHEAST = "SSE"
119
- SOUTH = "S"
120
- SOUTH_SOUTHWEST = "SSW"
121
- SOUTHWEST = "SW"
122
- WEST_SOUTHWEST = "WSW"
123
- WEST = "W"
124
- WEST_NORTHWEST = "WNW"
125
- NORTHWEST = "NW"
126
- NORTH_NORTHWEST = "NNW"
127
-
114
+
115
+ NORTH = 'N'
116
+ NORTH_NORTHEAST = 'NNE'
117
+ NORTHEAST = 'NE'
118
+ EAST_NORTHEAST = 'ENE'
119
+ EAST = 'E'
120
+ EAST_SOUTHEAST = 'ESE'
121
+ SOUTHEAST = 'SE'
122
+ SOUTH_SOUTHEAST = 'SSE'
123
+ SOUTH = 'S'
124
+ SOUTH_SOUTHWEST = 'SSW'
125
+ SOUTHWEST = 'SW'
126
+ WEST_SOUTHWEST = 'WSW'
127
+ WEST = 'W'
128
+ WEST_NORTHWEST = 'WNW'
129
+ NORTHWEST = 'NW'
130
+ NORTH_NORTHWEST = 'NNW'
131
+
128
132
  def _new(value: str, degrees: float):
129
133
  enum = WindDirection(value)
130
134
  enum.__degrees = degrees
131
-
135
+
132
136
  return enum
133
-
134
- def __contains__(self, other: Union["WindDirection", float, int]) -> bool:
137
+
138
+ def __contains__(self, other: Union['WindDirection', float, int]) -> bool:
135
139
  if isinstance(other, self.__class__):
136
140
  other = other.degrees
137
-
141
+
138
142
  if self is self.NORTH:
139
143
  return other > 348.75 or other <= 11.25
140
144
  elif self is self.NORTH_NORTHEAST:
@@ -167,27 +171,28 @@ class WindDirection(BasicEnum):
167
171
  return 303.75 < other <= 326.25
168
172
  else:
169
173
  return 326.25 < other <= 348.75
170
-
174
+
171
175
  def __float__(self) -> float:
172
176
  return self.__degrees
173
-
177
+
174
178
  @property
175
179
  def degrees(self) -> int:
176
180
  """The wind direction's value in degrees."""
177
-
181
+
178
182
  return self.__degrees
179
-
183
+
180
184
  @property
181
185
  def emoji(self) -> str:
182
186
  """The emoji representing this enum."""
183
-
187
+
184
188
  return WIND_DIRECTION_EMOJIS[int(((self.__degrees + 22.5) % 360) / 45.0)]
185
189
 
190
+
186
191
  class Locale(Enum):
187
192
  """Represents the list of supported locales/languages by this library."""
188
-
193
+
189
194
  __slots__ = ()
190
-
195
+
191
196
  AFRIKAANS = 'af'
192
197
  AMHARIC = 'am'
193
198
  ARABIC = 'ar'
@@ -260,19 +265,20 @@ class Locale(Enum):
260
265
  VIETNAMESE = 'vi'
261
266
  WELSH = 'cy'
262
267
  ZULU = 'zu'
263
-
268
+
264
269
  def __repr__(self) -> str:
265
270
  return f'{self.__class__.__name__}.{self.name}'
266
-
271
+
267
272
  def __str__(self) -> str:
268
273
  arr = self.name.title().split('_')
269
274
  return f'{arr[:-1].join(" ")} ({arr[-1]})' if len(arr) != 1 else arr[0]
270
275
 
276
+
271
277
  class Kind(BasicEnum):
272
278
  """Represents a weather forecast kind."""
273
-
279
+
274
280
  __slots__ = ()
275
-
281
+
276
282
  SUNNY = 113
277
283
  PARTLY_CLOUDY = 116
278
284
  CLOUDY = 119
@@ -291,7 +297,7 @@ class Kind(BasicEnum):
291
297
  HEAVY_SNOW_SHOWERS = 335
292
298
  THUNDERY_HEAVY_RAIN = 389
293
299
  THUNDERY_SNOW_SHOWERS = 392
294
-
300
+
295
301
  @classmethod
296
302
  def _missing_(self, value: int):
297
303
  if value == 248 or value == 260:
@@ -300,7 +306,16 @@ class Kind(BasicEnum):
300
306
  return self.LIGHT_SHOWERS
301
307
  elif value == 362 or value == 365 or value == 374:
302
308
  return self.LIGHT_SLEET_SHOWERS
303
- elif value == 185 or value == 281 or value == 284 or value == 311 or value == 314 or value == 317 or value == 350 or value == 377:
309
+ elif (
310
+ value == 185
311
+ or value == 281
312
+ or value == 284
313
+ or value == 311
314
+ or value == 314
315
+ or value == 317
316
+ or value == 350
317
+ or value == 377
318
+ ):
304
319
  return self.LIGHT_SLEET
305
320
  elif value == 386:
306
321
  return self.THUNDERY_SHOWERS
@@ -318,11 +333,11 @@ class Kind(BasicEnum):
318
333
  return self.LIGHT_SNOW_SHOWERS
319
334
  elif value == 371 or value == 395:
320
335
  return self.HEAVY_SNOW_SHOWERS
321
-
336
+
322
337
  @property
323
338
  def emoji(self) -> str:
324
339
  """The emoji representing this enum."""
325
-
340
+
326
341
  if self is self.CLOUDY:
327
342
  return '☁️'
328
343
  elif self is self.FOG:
@@ -362,11 +377,12 @@ class Kind(BasicEnum):
362
377
  else:
363
378
  return '✨'
364
379
 
380
+
365
381
  class Phase(BasicEnum):
366
382
  """Represents a moon phase."""
367
-
383
+
368
384
  __slots__ = ()
369
-
385
+
370
386
  NEW_MOON = 'New Moon'
371
387
  WAXING_CRESCENT = 'Waxing Crescent'
372
388
  FIRST_QUARTER = 'First Quarter'
@@ -375,11 +391,11 @@ class Phase(BasicEnum):
375
391
  WANING_GIBBOUS = 'Waning Gibbous'
376
392
  LAST_QUARTER = 'Last Quarter'
377
393
  WANING_CRESCENT = 'Waning Crescent'
378
-
394
+
379
395
  @property
380
396
  def emoji(self) -> str:
381
397
  """The stylized name for this enum."""
382
-
398
+
383
399
  if self is self.NEW_MOON:
384
400
  return '🌑'
385
401
  elif self is self.WAXING_CRESCENT:
@@ -1,4 +1,4 @@
1
1
  class Error(Exception):
2
2
  """Represents a ``python_weather`` error class. Extends :py:class:`Exception`."""
3
-
4
- __slots__ = ()
3
+
4
+ __slots__ = ()
@@ -1,315 +1,315 @@
1
1
  from typing import Iterable, Optional, Tuple
2
2
  from datetime import datetime, date, time
3
- from enum import auto
4
3
 
5
4
  from .base import BaseForecast, CustomizableBase
6
5
  from .enums import Phase, Locale, HeatIndex
7
6
  from .constants import _Unit, LATLON_REGEX
8
7
 
8
+
9
9
  class HourlyForecast(BaseForecast):
10
10
  """Represents a weather forecast of a specific hour."""
11
-
11
+
12
12
  __slots__ = ()
13
-
13
+
14
14
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
15
15
  # for inheritance purposes
16
16
  if 'temp_C' not in json:
17
17
  json['temp_C'] = json.pop('tempC')
18
18
  if 'temp_F' not in json:
19
19
  json['temp_F'] = json.pop('tempF')
20
-
20
+
21
21
  super().__init__(json, unit, locale)
22
-
22
+
23
23
  def __repr__(self) -> str:
24
24
  return f'<{self.__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
25
-
25
+
26
26
  @property
27
27
  def dew_point(self) -> int:
28
28
  """The dew point in either Celcius or Fahrenheit."""
29
-
29
+
30
30
  return int(
31
31
  self._BaseForecast__inner[f'DewPoint{self._CustomizableBase__unit.temperature}']
32
- ) # yapf: disable
33
-
32
+ )
33
+
34
34
  @property
35
35
  def heat_index(self) -> HeatIndex:
36
36
  """The heat index in either Celcius or Fahrenheit."""
37
-
37
+
38
38
  celcius_index = int(self._BaseForecast__inner['HeatIndexC'])
39
-
39
+
40
40
  return HeatIndex._new(
41
41
  celcius_index,
42
- int(self._BaseForecast__inner[f'HeatIndex{self._CustomizableBase__unit.temperature}'])
43
- ) # yapf: disable
44
-
42
+ int(
43
+ self._BaseForecast__inner[
44
+ f'HeatIndex{self._CustomizableBase__unit.temperature}'
45
+ ]
46
+ ),
47
+ )
48
+
45
49
  @property
46
50
  def wind_chill(self) -> int:
47
51
  """The wind chill value in either Celcius or Fahrenheit."""
48
-
52
+
49
53
  return int(
50
54
  self._BaseForecast__inner[f'WindChill{self._CustomizableBase__unit.temperature}']
51
- ) # yapf: disable
52
-
55
+ )
56
+
53
57
  @property
54
58
  def wind_gust(self) -> int:
55
59
  """The wind gust value in either Kilometers per hour or Miles per hour."""
56
-
60
+
57
61
  key = f'WindGust{self._CustomizableBase__unit.velocity}'
58
62
  return int(self._BaseForecast__inner[key])
59
-
63
+
60
64
  @property
61
65
  def chances_of_fog(self) -> int:
62
66
  """Chances of a fog in percent."""
63
-
67
+
64
68
  return int(self._BaseForecast__inner['chanceoffog'])
65
-
69
+
66
70
  @property
67
71
  def chances_of_frost(self) -> int:
68
72
  """Chances of a frost in percent."""
69
-
73
+
70
74
  return int(self._BaseForecast__inner['chanceoffrost'])
71
-
75
+
72
76
  @property
73
77
  def chances_of_high_temperature(self) -> int:
74
78
  """Chances of a high temperature in percent."""
75
-
79
+
76
80
  return int(self._BaseForecast__inner['chanceofhightemp'])
77
-
81
+
78
82
  @property
79
83
  def chances_of_overcast(self) -> int:
80
84
  """Chances of an overcast in percent."""
81
-
85
+
82
86
  return int(self._BaseForecast__inner['chanceofovercast'])
83
-
87
+
84
88
  @property
85
89
  def chances_of_rain(self) -> int:
86
90
  """Chances of a rain in percent."""
87
-
91
+
88
92
  return int(self._BaseForecast__inner['chanceofrain'])
89
-
93
+
90
94
  @property
91
95
  def chances_of_remaining_dry(self) -> int:
92
96
  """Chances of remaining dry in percent."""
93
-
97
+
94
98
  return int(self._BaseForecast__inner['chanceofremdry'])
95
-
99
+
96
100
  @property
97
101
  def chances_of_snow(self) -> int:
98
102
  """Chances of a snow in percent."""
99
-
103
+
100
104
  return int(self._BaseForecast__inner['chanceofsnow'])
101
-
105
+
102
106
  @property
103
107
  def chances_of_sunshine(self) -> int:
104
108
  """Chances of a sunshine in percent."""
105
-
109
+
106
110
  return int(self._BaseForecast__inner['chanceofsunshine'])
107
-
111
+
108
112
  @property
109
113
  def chances_of_thunder(self) -> int:
110
114
  """Chances of a thunder in percent."""
111
-
115
+
112
116
  return int(self._BaseForecast__inner['chanceofthunder'])
113
-
117
+
114
118
  @property
115
119
  def chances_of_windy(self) -> int:
116
120
  """Chances of windy in percent."""
117
-
121
+
118
122
  return int(self._BaseForecast__inner['chanceofwindy'])
119
-
123
+
120
124
  @property
121
125
  def cloud_cover(self) -> int:
122
126
  """The cloud cover value in percent."""
123
-
127
+
124
128
  return int(self._BaseForecast__inner['cloudcover'])
125
-
129
+
126
130
  @property
127
131
  def time(self) -> time:
128
132
  """The local time in hours and minutes."""
129
-
130
- return time() if len(self._BaseForecast__inner['time']) < 3 else datetime.strptime(self._BaseForecast__inner['time'], '%H%M').time() # yapf: disable
133
+
134
+ return (
135
+ time()
136
+ if len(self._BaseForecast__inner['time']) < 3
137
+ else datetime.strptime(self._BaseForecast__inner['time'], '%H%M').time()
138
+ )
139
+
131
140
 
132
141
  class DailyForecast(CustomizableBase):
133
142
  __slots__ = ('__inner', '__astronomy')
134
-
143
+
135
144
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
136
145
  self.__astronomy = json.pop('astronomy')[0]
137
146
  self.__inner = json
138
-
147
+
139
148
  super().__init__(unit, locale)
140
-
149
+
141
150
  def __repr__(self) -> str:
142
- return f'<{self.__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
143
-
151
+ return (
152
+ f'<{self.__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
153
+ )
154
+
144
155
  @property
145
156
  def moon_illumination(self) -> int:
146
157
  """The percentage of the moon illuminated."""
147
-
158
+
148
159
  return int(self.__astronomy['moon_illumination'])
149
-
160
+
150
161
  @property
151
162
  def moon_phase(self) -> Phase:
152
163
  """The moon's phase."""
153
-
164
+
154
165
  return Phase(self.__astronomy['moon_phase'])
155
-
166
+
156
167
  @property
157
168
  def moonrise(self) -> Optional[time]:
158
169
  """The local time when the moon rises. This can be ``None``."""
159
-
170
+
160
171
  try:
161
172
  return datetime.strptime(self.__astronomy['moonrise'], '%I:%M %p').time()
162
173
  except ValueError:
163
174
  ...
164
-
175
+
165
176
  @property
166
177
  def moonset(self) -> Optional[time]:
167
178
  """The local time when the moon sets. This can be ``None``."""
168
-
179
+
169
180
  try:
170
181
  return datetime.strptime(self.__astronomy['moonset'], '%I:%M %p').time()
171
182
  except ValueError:
172
183
  ...
173
-
184
+
174
185
  @property
175
186
  def sunrise(self) -> Optional[time]:
176
187
  """The local time when the sun rises. This can be ``None``."""
177
-
188
+
178
189
  try:
179
190
  return datetime.strptime(self.__astronomy['sunrise'], '%I:%M %p').time()
180
191
  except ValueError:
181
192
  ...
182
-
193
+
183
194
  @property
184
195
  def sunset(self) -> Optional[time]:
185
196
  """The local time when the sun sets. This can be ``None``."""
186
-
197
+
187
198
  try:
188
199
  return datetime.strptime(self.__astronomy['sunset'], '%I:%M %p').time()
189
200
  except ValueError:
190
201
  ...
191
-
202
+
192
203
  @property
193
204
  def date(self) -> date:
194
205
  """The local date of this forecast."""
195
-
206
+
196
207
  return datetime.strptime(self.__inner['date'], '%Y-%m-%d').date()
197
-
208
+
198
209
  @property
199
210
  def lowest_temperature(self) -> int:
200
211
  """The lowest temperature in either Celcius or Fahrenheit."""
201
-
202
- return int(
203
- self.__inner[f'mintemp{self._CustomizableBase__unit.temperature}']
204
- ) # yapf: disable
205
-
212
+
213
+ return int(self.__inner[f'mintemp{self._CustomizableBase__unit.temperature}'])
214
+
206
215
  @property
207
216
  def highest_temperature(self) -> int:
208
217
  """The highest temperature in either Celcius or Fahrenheit."""
209
-
210
- return int(
211
- self.__inner[f'maxtemp{self._CustomizableBase__unit.temperature}']
212
- ) # yapf: disable
213
-
218
+
219
+ return int(self.__inner[f'maxtemp{self._CustomizableBase__unit.temperature}'])
220
+
214
221
  @property
215
222
  def temperature(self) -> int:
216
223
  """The average temperature in either Celcius or Fahrenheit."""
217
-
218
- return int(
219
- self.__inner[f'avgtemp{self._CustomizableBase__unit.temperature}']
220
- ) # yapf: disable
221
-
224
+
225
+ return int(self.__inner[f'avgtemp{self._CustomizableBase__unit.temperature}'])
226
+
222
227
  @property
223
228
  def sunlight(self) -> float:
224
229
  """Hours of sunlight."""
225
-
230
+
226
231
  return float(self.__inner['sunHour'])
227
-
232
+
228
233
  @property
229
234
  def snowfall(self) -> float:
230
235
  """Total snowfall in either Centimeters or Inches."""
231
-
232
- return float(
233
- self.__inner['totalSnow_cm']
234
- ) / self._CustomizableBase__unit.cm_divisor
235
-
236
+
237
+ return float(self.__inner['totalSnow_cm']) / self._CustomizableBase__unit.cm_divisor
238
+
236
239
  @property
237
240
  def hourly_forecasts(self) -> Iterable[HourlyForecast]:
238
241
  """The hourly forecasts of this day."""
239
-
242
+
240
243
  return (
241
- HourlyForecast(
242
- elem, self._CustomizableBase__unit, self._CustomizableBase__locale
243
- ) for elem in self.__inner['hourly']
244
+ HourlyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
245
+ for elem in self.__inner['hourly']
244
246
  )
245
247
 
248
+
246
249
  class Forecast(BaseForecast):
247
250
  """Represents today's weather forecast, alongside daily and hourly weather forecasts."""
248
-
251
+
249
252
  __slots__ = ('__inner', '__nearest')
250
-
253
+
251
254
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
252
255
  current = json['current_condition'][0]
253
256
  self.__nearest = json.pop('nearest_area')[0]
254
257
  self.__inner = json
255
-
258
+
256
259
  super().__init__(current, unit, locale)
257
-
260
+
258
261
  def __repr__(self) -> str:
259
262
  return f'<{self.__class__.__name__} location={self.location!r} datetime={self.datetime!r} temperature={self.temperature!r}>'
260
-
263
+
261
264
  @property
262
265
  def local_population(self) -> int:
263
266
  """The local population count."""
264
-
267
+
265
268
  return int(self.__nearest['population'])
266
-
269
+
267
270
  @property
268
271
  def region(self) -> str:
269
272
  """The local region's name."""
270
-
273
+
271
274
  return self.__nearest['region'][0]['value']
272
-
275
+
273
276
  @property
274
277
  def location(self) -> str:
275
278
  """The location's name."""
276
-
279
+
277
280
  return self.__nearest['areaName'][0]['value']
278
-
281
+
279
282
  @property
280
283
  def country(self) -> str:
281
284
  """The local country's name."""
282
-
285
+
283
286
  return self.__nearest['country'][0]['value']
284
-
287
+
285
288
  @property
286
289
  def datetime(self) -> datetime:
287
290
  """The local date and time of this weather forecast."""
288
-
291
+
289
292
  return datetime.strptime(
290
293
  self._BaseForecast__inner['localObsDateTime'], '%Y-%m-%d %I:%M %p'
291
294
  )
292
-
295
+
293
296
  @property
294
297
  def daily_forecasts(self) -> Iterable[DailyForecast]:
295
298
  """Daily weather forecasts in this location."""
296
-
299
+
297
300
  return (
298
- DailyForecast(
299
- elem, self._CustomizableBase__unit, self._CustomizableBase__locale
300
- ) for elem in self.__inner['weather']
301
+ DailyForecast(elem, self._CustomizableBase__unit, self._CustomizableBase__locale)
302
+ for elem in self.__inner['weather']
301
303
  )
302
-
304
+
303
305
  @property
304
306
  def coordinates(self) -> Tuple[float, float]:
305
307
  """A tuple of this forecast's latitude and longitude."""
306
-
308
+
307
309
  try:
308
- for req in filter(
309
- lambda x: x['type'] == 'LatLon', self.__inner['request']
310
- ):
310
+ for req in filter(lambda x: x['type'] == 'LatLon', self.__inner['request']):
311
311
  lat, lon = LATLON_REGEX.findall(req['query'])[0]
312
-
312
+
313
313
  return float(lat), float(lon)
314
314
  except:
315
- return float(self.__nearest['latitude']), float(self.__nearest['longitude']) # yapf: disable
315
+ return float(self.__nearest['latitude']), float(self.__nearest['longitude'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-weather
3
- Version: 2.0.0
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
File without changes
File without changes
File without changes