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