python-weather 2.0.3__tar.gz → 2.0.4__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.
Files changed (22) hide show
  1. {python_weather-2.0.3/python_weather.egg-info → python_weather-2.0.4}/PKG-INFO +16 -10
  2. {python_weather-2.0.3 → python_weather-2.0.4}/README.md +12 -6
  3. {python_weather-2.0.3 → python_weather-2.0.4}/pyproject.toml +6 -6
  4. python_weather-2.0.4/python_weather/__init__.py +47 -0
  5. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather/base.py +35 -10
  6. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather/client.py +46 -20
  7. python_weather-2.0.4/python_weather/constants.py +63 -0
  8. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather/enums.py +51 -40
  9. python_weather-2.0.4/python_weather/errors.py +45 -0
  10. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather/forecast.py +57 -22
  11. {python_weather-2.0.3 → python_weather-2.0.4/python_weather.egg-info}/PKG-INFO +16 -10
  12. python_weather-2.0.4/python_weather.egg-info/requires.txt +1 -0
  13. python_weather-2.0.3/python_weather/__init__.py +0 -23
  14. python_weather-2.0.3/python_weather/constants.py +0 -39
  15. python_weather-2.0.3/python_weather/errors.py +0 -4
  16. python_weather-2.0.3/python_weather.egg-info/requires.txt +0 -1
  17. {python_weather-2.0.3 → python_weather-2.0.4}/LICENSE +0 -0
  18. {python_weather-2.0.3 → python_weather-2.0.4}/MANIFEST.in +0 -0
  19. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather.egg-info/SOURCES.txt +0 -0
  20. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather.egg-info/dependency_links.txt +0 -0
  21. {python_weather-2.0.3 → python_weather-2.0.4}/python_weather.egg-info/top_level.txt +0 -0
  22. {python_weather-2.0.3 → python_weather-2.0.4}/setup.cfg +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-weather
3
- Version: 2.0.3
3
+ Version: 2.0.4
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: Donations, https://ko-fi.com/null8626
8
- Project-URL: Changelog, https://python-weather.readthedocs.io/en/latest/changelog.html
9
- Project-URL: Homepage, https://python-weather.readthedocs.io/en/latest/
10
7
  Project-URL: Documentation, https://python-weather.readthedocs.io/en/latest/
11
8
  Project-URL: Repository, https://github.com/null8626/python-weather
9
+ Project-URL: Changelog, https://python-weather.readthedocs.io/en/latest/changelog.html
10
+ Project-URL: Donate via GitHub Sponsors, https://github.com/sponsors/null8626
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
14
  Classifier: Framework :: aiohttp
@@ -35,14 +35,16 @@ Classifier: Programming Language :: Python :: 3.12
35
35
  Requires-Python: >=3.8
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
- Requires-Dist: aiohttp==3.9.5
38
+ Requires-Dist: aiohttp>=3.10.8
39
39
 
40
- # [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
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
 
42
42
  [pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
43
43
  [pypi-url]: https://pypi.org/project/python-weather/
44
44
  [downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
45
45
  [ko-fi-brief-image]: https://img.shields.io/badge/donations-ko--fi-red?color=ff5e5b&style=flat-square
46
+ [codacy-url]: https://app.codacy.com/project/badge/Grade/0f7721b7e4314a748c75a04f0a7e0ce3
47
+ [codacy-image]: https://app.codacy.com/gh/null8626/python-weather/dashboard
46
48
  [ko-fi-image]: https://ko-fi.com/img/githubbutton_sm.svg
47
49
  [ko-fi-url]: https://ko-fi.com/null8626
48
50
 
@@ -51,7 +53,7 @@ A free and asynchronous weather Python API wrapper made in Python, for Python.
51
53
  ## Installation
52
54
 
53
55
  ```console
54
- $ pip install python-weather
56
+ pip install python-weather
55
57
  ```
56
58
 
57
59
  ## Example
@@ -65,7 +67,7 @@ import python_weather
65
67
  import asyncio
66
68
  import os
67
69
 
68
- async def getweather():
70
+ async def getweather() -> None:
69
71
  # declare the client. the measuring unit used defaults to the metric system (celcius, km/h, etc.)
70
72
  async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
71
73
  # fetch a weather forecast from a city
@@ -75,11 +77,11 @@ async def getweather():
75
77
  print(weather.temperature)
76
78
 
77
79
  # get the weather forecast for a few days
78
- for daily in weather.daily_forecasts:
80
+ for daily in weather:
79
81
  print(daily)
80
82
 
81
83
  # hourly forecasts
82
- for hourly in daily.hourly_forecasts:
84
+ for hourly in daily:
83
85
  print(f' --> {hourly!r}')
84
86
 
85
87
  if __name__ == '__main__':
@@ -91,6 +93,10 @@ if __name__ == '__main__':
91
93
  asyncio.run(getweather())
92
94
  ```
93
95
 
96
+ ## Data source
97
+
98
+ This library depends on [`wttr.in`](https://github.com/chubin/wttr.in), which uses data from the [World Weather Online API](https://www.worldweatheronline.com/weather-api/).
99
+
94
100
  ## Donations
95
101
 
96
102
  If you want to support this project, consider donating! ❤
@@ -1,9 +1,11 @@
1
- # [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
1
+ # [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]
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
6
  [ko-fi-brief-image]: https://img.shields.io/badge/donations-ko--fi-red?color=ff5e5b&style=flat-square
7
+ [codacy-url]: https://app.codacy.com/project/badge/Grade/0f7721b7e4314a748c75a04f0a7e0ce3
8
+ [codacy-image]: https://app.codacy.com/gh/null8626/python-weather/dashboard
7
9
  [ko-fi-image]: https://ko-fi.com/img/githubbutton_sm.svg
8
10
  [ko-fi-url]: https://ko-fi.com/null8626
9
11
 
@@ -12,7 +14,7 @@ A free and asynchronous weather Python API wrapper made in Python, for Python.
12
14
  ## Installation
13
15
 
14
16
  ```console
15
- $ pip install python-weather
17
+ pip install python-weather
16
18
  ```
17
19
 
18
20
  ## Example
@@ -26,7 +28,7 @@ import python_weather
26
28
  import asyncio
27
29
  import os
28
30
 
29
- async def getweather():
31
+ async def getweather() -> None:
30
32
  # declare the client. the measuring unit used defaults to the metric system (celcius, km/h, etc.)
31
33
  async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
32
34
  # fetch a weather forecast from a city
@@ -36,11 +38,11 @@ async def getweather():
36
38
  print(weather.temperature)
37
39
 
38
40
  # get the weather forecast for a few days
39
- for daily in weather.daily_forecasts:
41
+ for daily in weather:
40
42
  print(daily)
41
43
 
42
44
  # hourly forecasts
43
- for hourly in daily.hourly_forecasts:
45
+ for hourly in daily:
44
46
  print(f' --> {hourly!r}')
45
47
 
46
48
  if __name__ == '__main__':
@@ -52,8 +54,12 @@ if __name__ == '__main__':
52
54
  asyncio.run(getweather())
53
55
  ```
54
56
 
57
+ ## Data source
58
+
59
+ This library depends on [`wttr.in`](https://github.com/chubin/wttr.in), which uses data from the [World Weather Online API](https://www.worldweatheronline.com/weather-api/).
60
+
55
61
  ## Donations
56
62
 
57
63
  If you want to support this project, consider donating! ❤
58
64
 
59
- [![ko-fi][ko-fi-image]][ko-fi-url]
65
+ [![ko-fi][ko-fi-image]][ko-fi-url]
@@ -3,13 +3,13 @@ requires = ["setuptools"]
3
3
 
4
4
  [project]
5
5
  name = "python-weather"
6
- version = "2.0.3"
6
+ version = "2.0.4"
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.9.5"]
12
+ dependencies = ["aiohttp>=3.10.8"]
13
13
  classifiers = [
14
14
  "Development Status :: 5 - Production/Stable",
15
15
  "Framework :: aiohttp",
@@ -37,8 +37,8 @@ classifiers = [
37
37
  requires-python = ">=3.8"
38
38
 
39
39
  [project.urls]
40
- Donations = "https://ko-fi.com/null8626"
41
- Changelog = "https://python-weather.readthedocs.io/en/latest/changelog.html"
42
- Homepage = "https://python-weather.readthedocs.io/en/latest/"
43
40
  Documentation = "https://python-weather.readthedocs.io/en/latest/"
44
- Repository = "https://github.com/null8626/python-weather"
41
+ Repository = "https://github.com/null8626/python-weather"
42
+ Changelog = "https://python-weather.readthedocs.io/en/latest/changelog.html"
43
+ "Donate via GitHub Sponsors" = "https://github.com/sponsors/null8626"
44
+ "Donate via Ko-fi" = "https://ko-fi.com/null8626"
@@ -0,0 +1,47 @@
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 .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
26
+ from .constants import METRIC, IMPERIAL
27
+ from .errors import Error, RequestError
28
+ from .client import Client
29
+
30
+ __title__ = 'python-weather'
31
+ __author__ = 'null8626'
32
+ __license__ = 'MIT'
33
+ __copyright__ = 'Copyright (c) 2021-2024 null8626'
34
+ __version__ = '2.0.4'
35
+ __all__ = (
36
+ 'METRIC',
37
+ 'IMPERIAL',
38
+ 'Client',
39
+ 'Error',
40
+ 'RequestError',
41
+ 'HeatIndex',
42
+ 'Kind',
43
+ 'Locale',
44
+ 'Phase',
45
+ 'UltraViolet',
46
+ 'WindDirection',
47
+ )
@@ -1,4 +1,29 @@
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
+
1
25
  from enum import auto
26
+ from typing import Tuple
2
27
 
3
28
  from .errors import Error
4
29
  from .enums import WindDirection, Kind, Locale, UltraViolet
@@ -6,7 +31,7 @@ from .constants import _Unit
6
31
 
7
32
 
8
33
  class CustomizableBase:
9
- __slots__ = ('__unit', '__locale')
34
+ __slots__: Tuple[str, ...] = ('__unit', '__locale')
10
35
 
11
36
  def __init__(self, unit: _Unit, locale: Locale):
12
37
  self.unit = unit
@@ -19,7 +44,7 @@ class CustomizableBase:
19
44
  return self.__unit
20
45
 
21
46
  @unit.setter
22
- def unit(self, to: _Unit):
47
+ def unit(self, to: _Unit) -> None:
23
48
  """
24
49
  Sets the default measuring unit used to display information in this object.
25
50
 
@@ -39,7 +64,7 @@ class CustomizableBase:
39
64
  return self.__locale
40
65
 
41
66
  @locale.setter
42
- def locale(self, to: Locale):
67
+ def locale(self, to: Locale) -> None:
43
68
  """
44
69
  Sets the default localization used to display information in this object.
45
70
 
@@ -55,7 +80,7 @@ class CustomizableBase:
55
80
 
56
81
 
57
82
  class BaseForecast(CustomizableBase):
58
- __slots__ = ('__inner',)
83
+ __slots__: Tuple[str, ...] = ('__inner',)
59
84
 
60
85
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
61
86
  self.__inner = json
@@ -70,7 +95,7 @@ class BaseForecast(CustomizableBase):
70
95
 
71
96
  @property
72
97
  def feels_like(self) -> int:
73
- """What it felt like, in Celcius or Fahrenheit."""
98
+ """What it felt like, in celcius or fahrenheit."""
74
99
 
75
100
  return int(self.__inner[f'FeelsLike{self._CustomizableBase__unit.temperature}'])
76
101
 
@@ -82,31 +107,31 @@ class BaseForecast(CustomizableBase):
82
107
 
83
108
  @property
84
109
  def temperature(self) -> int:
85
- """The temperature in either Celcius or Fahrenheit."""
110
+ """The temperature in either celcius or Fahrenheit."""
86
111
 
87
112
  return int(self.__inner[f'temp_{self._CustomizableBase__unit.temperature}'])
88
113
 
89
114
  @property
90
115
  def precipitation(self) -> float:
91
- """The precipitation in either Millimeters or Inches."""
116
+ """The precipitation in either millimeters or inches."""
92
117
 
93
118
  return float(self.__inner[f'precip{self._CustomizableBase__unit.precipitation}'])
94
119
 
95
120
  @property
96
121
  def pressure(self) -> float:
97
- """The pressure in either Pascal or Inches."""
122
+ """The pressure in either pascal or inches."""
98
123
 
99
124
  return float(self.__inner[f'pressure{self._CustomizableBase__unit.pressure}'])
100
125
 
101
126
  @property
102
127
  def visibility(self) -> int:
103
- """The visibility distance in either Kilometers or Miles."""
128
+ """The visibility distance in either kilometers or miles."""
104
129
 
105
130
  return int(self.__inner[f'visibility{self._CustomizableBase__unit.visibility}'])
106
131
 
107
132
  @property
108
133
  def wind_speed(self) -> int:
109
- """The wind speeds in either Kilometers per hour or Miles per hour."""
134
+ """The wind speeds in either kilometers/hour or miles/hour."""
110
135
 
111
136
  return int(self.__inner[f'windspeed{self._CustomizableBase__unit.velocity}'])
112
137
 
@@ -1,13 +1,37 @@
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
+
1
25
  from aiohttp import ClientSession, ClientTimeout, TCPConnector
2
26
  from urllib.parse import quote_plus
3
- from typing import Optional
27
+ from typing import Optional, Tuple
4
28
  from asyncio import sleep
5
29
  from enum import auto
6
30
 
31
+ from .errors import Error, RequestError
7
32
  from .constants import _Unit, METRIC
8
33
  from .base import CustomizableBase
9
34
  from .forecast import Forecast
10
- from .errors import Error
11
35
  from .enums import Locale
12
36
 
13
37
 
@@ -22,10 +46,10 @@ class Client(CustomizableBase):
22
46
  :param session: Whether to use an existing aiohttp client session for requesting or not. Defaults to ``None`` (creates a new one instead)
23
47
  :type session: Optional[:class:`aiohttp.ClientSession`]
24
48
 
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.
49
+ :raises Error: If ``unit`` is not ``METRIC`` or ``IMPERIAL``, or if ``locale`` is not ``None`` and not a part of the :class:`Locale` enum.
26
50
  """
27
51
 
28
- __slots__ = ('__session',)
52
+ __slots__: Tuple[str, ...] = ('__own_session', '__session')
29
53
 
30
54
  def __init__(
31
55
  self,
@@ -36,13 +60,15 @@ class Client(CustomizableBase):
36
60
  ):
37
61
  super().__init__(unit, locale)
38
62
 
63
+ self.__own_session = session is None
39
64
  self.__session = session or ClientSession(
65
+ raise_for_status=True,
40
66
  timeout=ClientTimeout(total=5000.0),
41
67
  connector=TCPConnector(verify_ssl=False),
42
68
  )
43
69
 
44
70
  def __repr__(self) -> str:
45
- return f'<{self.__class__.__name__} {self.__session!r}>'
71
+ return f'<{__class__.__name__} {self.__session!r}>'
46
72
 
47
73
  async def get(
48
74
  self,
@@ -56,9 +82,9 @@ class Client(CustomizableBase):
56
82
 
57
83
  :param location: The requested location name for said weather forecast.
58
84
  :type location: str
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`).
85
+ :param unit: Overrides the unit used by this object. Defaults to the one used by this object.
60
86
  :type unit: Optional[:py:class:`enum.auto`]
61
- :param locale: Overrides the locale/language used by the :class:`Client` object. Defaults to ``None`` (uses the one from the :class:`Client`).
87
+ :param locale: Overrides the locale used by this object. Defaults to the one used by this object.
62
88
  :type locale: Optional[Locale]
63
89
 
64
90
  :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.
@@ -82,25 +108,25 @@ class Client(CustomizableBase):
82
108
  delay = 0.5
83
109
 
84
110
  while True:
85
- async with self.__session.get(
86
- f'https://{subdomain}wttr.in/{quote_plus(location)}?format=j1'
87
- ) as resp:
88
- try:
111
+ try:
112
+ async with self.__session.get(
113
+ f'https://{subdomain}wttr.in/{quote_plus(location)}?format=j1'
114
+ ) as resp:
89
115
  return Forecast(await resp.json(), unit, locale)
90
- except Exception as e:
91
- if delay == 2:
92
- raise e # okay, that's too much requests - just raise the error
116
+ except Exception as err:
117
+ if delay == 4:
118
+ raise RequestError(err)
93
119
 
94
- await sleep(delay)
95
- delay *= 2
120
+ await sleep(delay)
121
+ delay *= 2
96
122
 
97
- async def close(self):
98
- """Closes the :class:`Client` object. Nothing will happen if it's already closed."""
123
+ async def close(self) -> None:
124
+ """Closes the :class:`Client` object. Nothing will happen if the client uses a pre-existing :class:`aiohttp.ClientSession` or if the session is already closed."""
99
125
 
100
- if not self.__session.closed:
126
+ if self.__own_session and not self.__session.closed:
101
127
  await self.__session.close()
102
128
 
103
- async def __aenter__(self):
129
+ async def __aenter__(self) -> 'Client':
104
130
  return self
105
131
 
106
132
  async def __aexit__(self, *_, **__):
@@ -0,0 +1,63 @@
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 Union, Tuple
26
+ import re
27
+
28
+
29
+ class _Unit:
30
+ __slots__: Tuple[str, ...] = (
31
+ 'temperature',
32
+ 'velocity',
33
+ 'pressure',
34
+ 'precipitation',
35
+ 'visibility',
36
+ 'cm_divisor',
37
+ )
38
+
39
+ def __init__(
40
+ self,
41
+ temperature: str,
42
+ velocity: str,
43
+ pressure: str,
44
+ precipitation: str,
45
+ visibility: str,
46
+ cm_divisor: Union[int, float],
47
+ ):
48
+ self.temperature = temperature
49
+ self.velocity = velocity
50
+ self.pressure = pressure
51
+ self.precipitation = precipitation
52
+ self.visibility = visibility
53
+ self.cm_divisor = cm_divisor
54
+
55
+ def __repr__(self) -> str:
56
+ return f'<Unit [{self.temperature}, {self.velocity}]>'
57
+
58
+
59
+ METRIC = _Unit('C', 'Kmph', '', 'MM', '', 1)
60
+ IMPERIAL = _Unit('F', 'Miles', 'Inches', 'Inches', 'Miles', 2.54)
61
+
62
+ WIND_DIRECTION_EMOJIS = ('↓', '↙', '←', '↖', '↑', '↗', '→', '↘')
63
+ LATLON_REGEX = re.compile(r'^Lat (\-?[\d\.]+) and Lon (\-?[\d\.]+)$')
@@ -1,11 +1,35 @@
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
+
1
25
  from enum import Enum
2
- from typing import Union
26
+ from typing import Union, Tuple
3
27
 
4
28
  from .constants import WIND_DIRECTION_EMOJIS
5
29
 
6
30
 
7
31
  class BasicEnum(Enum):
8
- __slots__ = ()
32
+ __slots__: Tuple[str, ...] = ()
9
33
 
10
34
  def __repr__(self) -> str:
11
35
  return f'{self.__class__.__name__}.{self.name}'
@@ -15,59 +39,45 @@ class BasicEnum(Enum):
15
39
 
16
40
 
17
41
  class IndexedEnum(Enum):
18
- __slots__ = ('__index',)
42
+ __slots__: Tuple[str, ...] = ('index',)
43
+
44
+ index: int
45
+ """The index value."""
19
46
 
20
47
  def __lt__(self, other: Union['IndexedEnum', int, float]) -> bool:
21
- if isinstance(other, self.__class__):
22
- return self.__index < other.index
23
- else:
24
- return self.__index < other
48
+ return self.index < getattr(other, 'index', other)
25
49
 
26
50
  def __eq__(self, other: Union['IndexedEnum', int, float]) -> bool:
27
- if isinstance(other, self.__class__):
28
- return self.__index == other.index
29
- else:
30
- return self.__index == other
51
+ return self.index == getattr(other, 'index', other)
31
52
 
32
53
  def __gt__(self, other: Union['IndexedEnum', int, float]) -> bool:
33
- if isinstance(other, self.__class__):
34
- return self.__index > other.index
35
- else:
36
- return self.__index > other
54
+ return self.index > getattr(other, 'index', other)
37
55
 
38
56
  def __hash__(self) -> int:
39
- return self.__index
57
+ return self.index
40
58
 
41
59
  def __int__(self) -> int:
42
- return self.__index
43
-
44
- @property
45
- def index(self) -> int:
46
- """The index value."""
47
-
48
- return self.__index
49
-
50
- @index.setter
51
- def index(self, new_index: int) -> int:
52
- self.__index = new_index
60
+ return self.index
53
61
 
54
62
 
55
63
  class HeatIndex(IndexedEnum):
56
64
  """Represents a heat index."""
57
65
 
66
+ __slots__: Tuple[str, ...] = ()
67
+
58
68
  CAUTION = None
59
69
  EXTREME_CAUTION = None
60
70
  DANGER = None
61
71
  EXTREME_DANGER = None
62
72
 
63
- def _new(celcius_index: int, true_index: int):
73
+ def _new(celcius_index: int, true_index: int) -> 'HeatIndex':
64
74
  enum = HeatIndex(celcius_index)
65
75
  enum.index = true_index
66
76
 
67
77
  return enum
68
78
 
69
79
  @classmethod
70
- def _missing_(self, celcius_index: int):
80
+ def _missing_(self, celcius_index: int) -> 'HeatIndex':
71
81
  if celcius_index <= 32:
72
82
  return self.CAUTION
73
83
  elif celcius_index <= 39:
@@ -81,20 +91,22 @@ class HeatIndex(IndexedEnum):
81
91
  class UltraViolet(BasicEnum, IndexedEnum):
82
92
  """Represents ultra-violet (UV) index."""
83
93
 
94
+ __slots__: Tuple[str, ...] = ()
95
+
84
96
  LOW = None
85
97
  MODERATE = None
86
98
  HIGH = None
87
99
  VERY_HIGH = None
88
100
  EXTREME = None
89
101
 
90
- def _new(index: int):
102
+ def _new(index: int) -> 'UltraViolet':
91
103
  enum = UltraViolet(index)
92
104
  enum.index = index
93
105
 
94
106
  return enum
95
107
 
96
108
  @classmethod
97
- def _missing_(self, index: int):
109
+ def _missing_(self, index: int) -> 'UltraViolet':
98
110
  if index <= 2:
99
111
  return self.LOW
100
112
  elif index <= 5:
@@ -110,7 +122,7 @@ class UltraViolet(BasicEnum, IndexedEnum):
110
122
  class WindDirection(BasicEnum):
111
123
  """Represents a wind direction."""
112
124
 
113
- __slots__ = ('__degrees',)
125
+ __slots__: Tuple[str, ...] = ('__degrees',)
114
126
 
115
127
  NORTH = 'N'
116
128
  NORTH_NORTHEAST = 'NNE'
@@ -129,15 +141,14 @@ class WindDirection(BasicEnum):
129
141
  NORTHWEST = 'NW'
130
142
  NORTH_NORTHWEST = 'NNW'
131
143
 
132
- def _new(value: str, degrees: float):
144
+ def _new(value: str, degrees: float) -> 'WindDirection':
133
145
  enum = WindDirection(value)
134
146
  enum.__degrees = degrees
135
147
 
136
148
  return enum
137
149
 
138
150
  def __contains__(self, other: Union['WindDirection', float, int]) -> bool:
139
- if isinstance(other, self.__class__):
140
- other = other.degrees
151
+ other = getattr(other, 'degrees', other)
141
152
 
142
153
  if self is self.NORTH:
143
154
  return other > 348.75 or other <= 11.25
@@ -191,7 +202,7 @@ class WindDirection(BasicEnum):
191
202
  class Locale(Enum):
192
203
  """Represents the list of supported locales/languages by this library."""
193
204
 
194
- __slots__ = ()
205
+ __slots__: Tuple[str, ...] = ()
195
206
 
196
207
  AFRIKAANS = 'af'
197
208
  AMHARIC = 'am'
@@ -267,7 +278,7 @@ class Locale(Enum):
267
278
  ZULU = 'zu'
268
279
 
269
280
  def __repr__(self) -> str:
270
- return f'{self.__class__.__name__}.{self.name}'
281
+ return f'{__class__.__name__}.{self.name}'
271
282
 
272
283
  def __str__(self) -> str:
273
284
  arr = self.name.title().split('_')
@@ -277,7 +288,7 @@ class Locale(Enum):
277
288
  class Kind(BasicEnum):
278
289
  """Represents a weather forecast kind."""
279
290
 
280
- __slots__ = ()
291
+ __slots__: Tuple[str, ...] = ()
281
292
 
282
293
  SUNNY = 113
283
294
  PARTLY_CLOUDY = 116
@@ -299,7 +310,7 @@ class Kind(BasicEnum):
299
310
  THUNDERY_SNOW_SHOWERS = 392
300
311
 
301
312
  @classmethod
302
- def _missing_(self, value: int):
313
+ def _missing_(self, value: int) -> 'Kind':
303
314
  if value == 248 or value == 260:
304
315
  return self.FOG
305
316
  elif value == 263 or value == 353:
@@ -381,7 +392,7 @@ class Kind(BasicEnum):
381
392
  class Phase(BasicEnum):
382
393
  """Represents a moon phase."""
383
394
 
384
- __slots__ = ()
395
+ __slots__: Tuple[str, ...] = ()
385
396
 
386
397
  NEW_MOON = 'New Moon'
387
398
  WAXING_CRESCENT = 'Waxing Crescent'
@@ -0,0 +1,45 @@
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 Tuple
26
+
27
+
28
+ class Error(Exception):
29
+ """Represents a ``python_weather`` error class. Extends :py:class:`Exception`."""
30
+
31
+ __slots__: Tuple[str, ...] = ()
32
+
33
+
34
+ class RequestError(Error):
35
+ """Thrown upon HTTP request failure. Extends :class:`Error`."""
36
+
37
+ __slots__: Tuple[str, ...] = ('source',)
38
+
39
+ source: Exception
40
+ """The :class:`Exception` instance causing this exception."""
41
+
42
+ def __init__(self, source: Exception):
43
+ self.source = source
44
+
45
+ super().__init__()
@@ -1,4 +1,28 @@
1
- from typing import Iterable, Optional, Tuple
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
2
26
  from datetime import datetime, date, time
3
27
 
4
28
  from .base import BaseForecast, CustomizableBase
@@ -9,7 +33,7 @@ from .constants import _Unit, LATLON_REGEX
9
33
  class HourlyForecast(BaseForecast):
10
34
  """Represents a weather forecast of a specific hour."""
11
35
 
12
- __slots__ = ()
36
+ __slots__: Tuple[str, ...] = ()
13
37
 
14
38
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
15
39
  # for inheritance purposes
@@ -21,11 +45,11 @@ class HourlyForecast(BaseForecast):
21
45
  super().__init__(json, unit, locale)
22
46
 
23
47
  def __repr__(self) -> str:
24
- return f'<{self.__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
48
+ return f'<{__class__.__name__} time={self.time!r} temperature={self.temperature!r} description={self.description!r} kind={self.kind!r}>'
25
49
 
26
50
  @property
27
51
  def dew_point(self) -> int:
28
- """The dew point in either Celcius or Fahrenheit."""
52
+ """The dew point in either celcius or fahrenheit."""
29
53
 
30
54
  return int(
31
55
  self._BaseForecast__inner[f'DewPoint{self._CustomizableBase__unit.temperature}']
@@ -33,7 +57,7 @@ class HourlyForecast(BaseForecast):
33
57
 
34
58
  @property
35
59
  def heat_index(self) -> HeatIndex:
36
- """The heat index in either Celcius or Fahrenheit."""
60
+ """The heat index in either celcius or fahrenheit."""
37
61
 
38
62
  celcius_index = int(self._BaseForecast__inner['HeatIndexC'])
39
63
 
@@ -48,7 +72,7 @@ class HourlyForecast(BaseForecast):
48
72
 
49
73
  @property
50
74
  def wind_chill(self) -> int:
51
- """The wind chill value in either Celcius or Fahrenheit."""
75
+ """The wind chill value in either celcius or fahrenheit."""
52
76
 
53
77
  return int(
54
78
  self._BaseForecast__inner[f'WindChill{self._CustomizableBase__unit.temperature}']
@@ -56,10 +80,11 @@ class HourlyForecast(BaseForecast):
56
80
 
57
81
  @property
58
82
  def wind_gust(self) -> int:
59
- """The wind gust value in either Kilometers per hour or Miles per hour."""
83
+ """The wind gust value in either kilometers/hour or miles/hour."""
60
84
 
61
- key = f'WindGust{self._CustomizableBase__unit.velocity}'
62
- return int(self._BaseForecast__inner[key])
85
+ return int(
86
+ self._BaseForecast__inner[f'WindGust{self._CustomizableBase__unit.velocity}']
87
+ )
63
88
 
64
89
  @property
65
90
  def chances_of_fog(self) -> int:
@@ -139,7 +164,7 @@ class HourlyForecast(BaseForecast):
139
164
 
140
165
 
141
166
  class DailyForecast(CustomizableBase):
142
- __slots__ = ('__inner', '__astronomy')
167
+ __slots__: Tuple[str, ...] = ('__inner', '__astronomy')
143
168
 
144
169
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
145
170
  self.__astronomy = json.pop('astronomy')[0]
@@ -148,9 +173,13 @@ class DailyForecast(CustomizableBase):
148
173
  super().__init__(unit, locale)
149
174
 
150
175
  def __repr__(self) -> str:
151
- return (
152
- f'<{self.__class__.__name__} date={self.date!r} temperature={self.temperature!r}>'
153
- )
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))
154
183
 
155
184
  @property
156
185
  def moon_illumination(self) -> int:
@@ -208,19 +237,19 @@ class DailyForecast(CustomizableBase):
208
237
 
209
238
  @property
210
239
  def lowest_temperature(self) -> int:
211
- """The lowest temperature in either Celcius or Fahrenheit."""
240
+ """The lowest temperature in either celcius or fahrenheit."""
212
241
 
213
242
  return int(self.__inner[f'mintemp{self._CustomizableBase__unit.temperature}'])
214
243
 
215
244
  @property
216
245
  def highest_temperature(self) -> int:
217
- """The highest temperature in either Celcius or Fahrenheit."""
246
+ """The highest temperature in either celcius or fahrenheit."""
218
247
 
219
248
  return int(self.__inner[f'maxtemp{self._CustomizableBase__unit.temperature}'])
220
249
 
221
250
  @property
222
251
  def temperature(self) -> int:
223
- """The average temperature in either Celcius or Fahrenheit."""
252
+ """The average temperature in either celcius or fahrenheit."""
224
253
 
225
254
  return int(self.__inner[f'avgtemp{self._CustomizableBase__unit.temperature}'])
226
255
 
@@ -232,7 +261,7 @@ class DailyForecast(CustomizableBase):
232
261
 
233
262
  @property
234
263
  def snowfall(self) -> float:
235
- """Total snowfall in either Centimeters or Inches."""
264
+ """Total snowfall in either centimeters or inches."""
236
265
 
237
266
  return float(self.__inner['totalSnow_cm']) / self._CustomizableBase__unit.cm_divisor
238
267
 
@@ -249,7 +278,7 @@ class DailyForecast(CustomizableBase):
249
278
  class Forecast(BaseForecast):
250
279
  """Represents today's weather forecast, alongside daily and hourly weather forecasts."""
251
280
 
252
- __slots__ = ('__inner', '__nearest')
281
+ __slots__: Tuple[str, ...] = ('__inner', '__nearest')
253
282
 
254
283
  def __init__(self, json: dict, unit: _Unit, locale: Locale):
255
284
  current = json['current_condition'][0]
@@ -259,7 +288,13 @@ class Forecast(BaseForecast):
259
288
  super().__init__(current, unit, locale)
260
289
 
261
290
  def __repr__(self) -> str:
262
- return f'<{self.__class__.__name__} location={self.location!r} datetime={self.datetime!r} temperature={self.temperature!r}>'
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))
263
298
 
264
299
  @property
265
300
  def local_population(self) -> int:
@@ -307,9 +342,9 @@ class Forecast(BaseForecast):
307
342
  """A tuple of this forecast's latitude and longitude."""
308
343
 
309
344
  try:
310
- for req in filter(lambda x: x['type'] == 'LatLon', self.__inner['request']):
311
- lat, lon = LATLON_REGEX.findall(req['query'])[0]
345
+ req = next(filter(lambda x: x['type'] == 'LatLon', self.__inner['request']))
346
+ match = LATLON_REGEX.match(req['query'])
312
347
 
313
- return float(lat), float(lon)
348
+ return float(match[1]), float(match[2])
314
349
  except:
315
350
  return float(self.__nearest['latitude']), float(self.__nearest['longitude'])
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-weather
3
- Version: 2.0.3
3
+ Version: 2.0.4
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: Donations, https://ko-fi.com/null8626
8
- Project-URL: Changelog, https://python-weather.readthedocs.io/en/latest/changelog.html
9
- Project-URL: Homepage, https://python-weather.readthedocs.io/en/latest/
10
7
  Project-URL: Documentation, https://python-weather.readthedocs.io/en/latest/
11
8
  Project-URL: Repository, https://github.com/null8626/python-weather
9
+ Project-URL: Changelog, https://python-weather.readthedocs.io/en/latest/changelog.html
10
+ Project-URL: Donate via GitHub Sponsors, https://github.com/sponsors/null8626
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
14
  Classifier: Framework :: aiohttp
@@ -35,14 +35,16 @@ Classifier: Programming Language :: Python :: 3.12
35
35
  Requires-Python: >=3.8
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
- Requires-Dist: aiohttp==3.9.5
38
+ Requires-Dist: aiohttp>=3.10.8
39
39
 
40
- # [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
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
 
42
42
  [pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
43
43
  [pypi-url]: https://pypi.org/project/python-weather/
44
44
  [downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
45
45
  [ko-fi-brief-image]: https://img.shields.io/badge/donations-ko--fi-red?color=ff5e5b&style=flat-square
46
+ [codacy-url]: https://app.codacy.com/project/badge/Grade/0f7721b7e4314a748c75a04f0a7e0ce3
47
+ [codacy-image]: https://app.codacy.com/gh/null8626/python-weather/dashboard
46
48
  [ko-fi-image]: https://ko-fi.com/img/githubbutton_sm.svg
47
49
  [ko-fi-url]: https://ko-fi.com/null8626
48
50
 
@@ -51,7 +53,7 @@ A free and asynchronous weather Python API wrapper made in Python, for Python.
51
53
  ## Installation
52
54
 
53
55
  ```console
54
- $ pip install python-weather
56
+ pip install python-weather
55
57
  ```
56
58
 
57
59
  ## Example
@@ -65,7 +67,7 @@ import python_weather
65
67
  import asyncio
66
68
  import os
67
69
 
68
- async def getweather():
70
+ async def getweather() -> None:
69
71
  # declare the client. the measuring unit used defaults to the metric system (celcius, km/h, etc.)
70
72
  async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
71
73
  # fetch a weather forecast from a city
@@ -75,11 +77,11 @@ async def getweather():
75
77
  print(weather.temperature)
76
78
 
77
79
  # get the weather forecast for a few days
78
- for daily in weather.daily_forecasts:
80
+ for daily in weather:
79
81
  print(daily)
80
82
 
81
83
  # hourly forecasts
82
- for hourly in daily.hourly_forecasts:
84
+ for hourly in daily:
83
85
  print(f' --> {hourly!r}')
84
86
 
85
87
  if __name__ == '__main__':
@@ -91,6 +93,10 @@ if __name__ == '__main__':
91
93
  asyncio.run(getweather())
92
94
  ```
93
95
 
96
+ ## Data source
97
+
98
+ This library depends on [`wttr.in`](https://github.com/chubin/wttr.in), which uses data from the [World Weather Online API](https://www.worldweatheronline.com/weather-api/).
99
+
94
100
  ## Donations
95
101
 
96
102
  If you want to support this project, consider donating! ❤
@@ -0,0 +1 @@
1
+ aiohttp>=3.10.8
@@ -1,23 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- from .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
4
- from .constants import METRIC, IMPERIAL
5
- from .client import Client
6
- from .errors import Error
7
-
8
- __title__ = 'python-weather'
9
- __author__ = 'null8626'
10
- __license__ = 'MIT'
11
- __version__ = '2.0.3'
12
- __all__ = (
13
- 'METRIC',
14
- 'IMPERIAL',
15
- 'Client',
16
- 'Error',
17
- 'HeatIndex',
18
- 'Kind',
19
- 'Locale',
20
- 'Phase',
21
- 'UltraViolet',
22
- 'WindDirection',
23
- )
@@ -1,39 +0,0 @@
1
- from typing import Union
2
- from re import compile
3
-
4
-
5
- class _Unit:
6
- __slots__ = (
7
- 'temperature',
8
- 'velocity',
9
- 'pressure',
10
- 'precipitation',
11
- 'visibility',
12
- 'cm_divisor',
13
- )
14
-
15
- def __init__(
16
- self,
17
- temperature: str,
18
- velocity: str,
19
- pressure: str,
20
- precipitation: str,
21
- visibility: str,
22
- cm_divisor: Union[int, float],
23
- ):
24
- self.temperature = temperature
25
- self.velocity = velocity
26
- self.pressure = pressure
27
- self.precipitation = precipitation
28
- self.visibility = visibility
29
- self.cm_divisor = cm_divisor
30
-
31
- def __repr__(self) -> str:
32
- return f'<Unit [{self.temperature}, {self.velocity}]>'
33
-
34
-
35
- METRIC = _Unit('C', 'Kmph', '', 'MM', '', 1)
36
- IMPERIAL = _Unit('F', 'Miles', 'Inches', 'Inches', 'Miles', 2.54)
37
-
38
- WIND_DIRECTION_EMOJIS = ('↓', '↙', '←', '↖', '↑', '↗', '→', '↘')
39
- LATLON_REGEX = compile(r'^Lat (\-?[\d\.]+) and Lon (\-?[\d\.]+)$')
@@ -1,4 +0,0 @@
1
- class Error(Exception):
2
- """Represents a ``python_weather`` error class. Extends :py:class:`Exception`."""
3
-
4
- __slots__ = ()
@@ -1 +0,0 @@
1
- aiohttp==3.9.5
File without changes
File without changes