python-weather 2.1.0__tar.gz → 2.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_weather-2.1.0 → python_weather-2.1.2}/LICENSE +1 -1
- {python_weather-2.1.0 → python_weather-2.1.2}/MANIFEST.in +8 -1
- {python_weather-2.1.0/python_weather.egg-info → python_weather-2.1.2}/PKG-INFO +104 -110
- {python_weather-2.1.0 → python_weather-2.1.2}/README.md +10 -15
- {python_weather-2.1.0 → python_weather-2.1.2}/pyproject.toml +17 -7
- python_weather-2.1.2/python_weather/__init__.py +34 -0
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather/base.py +15 -28
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather/client.py +58 -42
- python_weather-2.1.2/python_weather/constants.py +69 -0
- python_weather-2.1.2/python_weather/enums.py +333 -0
- python_weather-2.1.2/python_weather/errors.py +29 -0
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather/forecast.py +30 -49
- python_weather-2.1.2/python_weather/version.py +4 -0
- {python_weather-2.1.0 → python_weather-2.1.2/python_weather.egg-info}/PKG-INFO +104 -110
- python_weather-2.1.2/python_weather.egg-info/requires.txt +1 -0
- {python_weather-2.1.0 → python_weather-2.1.2}/setup.cfg +4 -4
- python_weather-2.1.0/python_weather/__init__.py +0 -50
- python_weather-2.1.0/python_weather/constants.py +0 -63
- python_weather-2.1.0/python_weather/enums.py +0 -425
- python_weather-2.1.0/python_weather/errors.py +0 -48
- python_weather-2.1.0/python_weather/version.py +0 -1
- python_weather-2.1.0/python_weather.egg-info/requires.txt +0 -1
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather.egg-info/SOURCES.txt +0 -0
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather.egg-info/dependency_links.txt +0 -0
- {python_weather-2.1.0 → python_weather-2.1.2}/python_weather.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
prune .pytest_cache
|
|
1
2
|
prune .ruff_cache
|
|
2
3
|
prune .github
|
|
4
|
+
prune tests
|
|
3
5
|
prune docs
|
|
4
6
|
exclude .gitattributes
|
|
5
7
|
exclude .gitignore
|
|
6
8
|
exclude .readthedocs.yml
|
|
9
|
+
exclude .coverage
|
|
7
10
|
exclude ruff.toml
|
|
8
11
|
exclude LICENSE
|
|
9
12
|
exclude changelog.yml
|
|
10
|
-
exclude
|
|
13
|
+
exclude codacy-coverage
|
|
14
|
+
exclude codecov*
|
|
15
|
+
exclude coverage.xml
|
|
16
|
+
exclude junit.xml
|
|
17
|
+
global-exclude __pycache__/*
|
|
@@ -1,110 +1,104 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python-weather
|
|
3
|
-
Version: 2.1.
|
|
4
|
-
Summary: A free and asynchronous weather API wrapper made in Python, for Python.
|
|
5
|
-
Author: null8626
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Documentation, https://python-weather.readthedocs.io/en/latest/
|
|
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
|
-
Keywords: weather,forecast,weather-
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Intended Audience :: Education
|
|
15
|
-
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier: Intended Audience :: Information Technology
|
|
17
|
-
Classifier: Natural Language :: English
|
|
18
|
-
Classifier: Operating System :: OS Independent
|
|
19
|
-
Classifier: Topic :: Education
|
|
20
|
-
Classifier: Topic :: Internet
|
|
21
|
-
Classifier: Topic :: Scientific/Engineering
|
|
22
|
-
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
23
|
-
Classifier: Topic :: Software Development
|
|
24
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
-
Classifier:
|
|
26
|
-
Classifier: Programming Language :: Python
|
|
27
|
-
Classifier: Programming Language :: Python :: 3
|
|
28
|
-
Classifier: Programming Language :: Python :: 3
|
|
29
|
-
Classifier: Programming Language :: Python :: 3.
|
|
30
|
-
Classifier: Programming Language :: Python :: 3.
|
|
31
|
-
Classifier: Programming Language :: Python :: 3.
|
|
32
|
-
Classifier: Programming Language :: Python :: 3.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
43
|
-
[pypi-
|
|
44
|
-
[
|
|
45
|
-
[
|
|
46
|
-
[
|
|
47
|
-
[
|
|
48
|
-
[ko-fi-
|
|
49
|
-
[ko-fi-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
import
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
async def main() -> None:
|
|
74
|
-
|
|
75
|
-
# Declare the client. The measuring unit used defaults to the metric system (celcius, km/h, etc.)
|
|
76
|
-
async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
|
|
77
|
-
|
|
78
|
-
# Fetch a weather forecast from a city.
|
|
79
|
-
weather = await client.get('New York')
|
|
80
|
-
|
|
81
|
-
# Fetch the temperature for today.
|
|
82
|
-
print(weather.temperature)
|
|
83
|
-
|
|
84
|
-
# Fetch weather forecast for upcoming days.
|
|
85
|
-
for daily in weather:
|
|
86
|
-
print(daily)
|
|
87
|
-
|
|
88
|
-
# Each daily forecast has their own hourly forecasts.
|
|
89
|
-
for hourly in daily:
|
|
90
|
-
print(f' --> {hourly!r}')
|
|
91
|
-
|
|
92
|
-
if __name__ == '__main__':
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
## Donations
|
|
107
|
-
|
|
108
|
-
If you want to support this project, consider donating! ❤
|
|
109
|
-
|
|
110
|
-
[![ko-fi][ko-fi-image]][ko-fi-url]
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-weather
|
|
3
|
+
Version: 2.1.2
|
|
4
|
+
Summary: A free and asynchronous weather API wrapper made in Python, for Python.
|
|
5
|
+
Author: null8626
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://python-weather.readthedocs.io/en/latest/
|
|
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
|
+
Keywords: weather,forecast,weather-forecast
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Topic :: Education
|
|
20
|
+
Classifier: Topic :: Internet
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
+
Classifier: Programming Language :: Python
|
|
26
|
+
Classifier: Programming Language :: Python :: 3
|
|
27
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
33
|
+
Requires-Python: >=3.10
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: aiohttp>=3.13.3
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![pypi downloads][pypi-downloads-image]][pypi-url] [![codacy-badge][codacy-image]][codacy-url] [![codecov-badge][codecov-image]][codecov-url] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
|
|
40
|
+
|
|
41
|
+
[pypi-url]: https://pypi.org/project/python-weather/
|
|
42
|
+
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
43
|
+
[pypi-downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
|
|
44
|
+
[codacy-url]: https://app.codacy.com/gh/null8626/python-weather/dashboard
|
|
45
|
+
[codacy-image]: https://app.codacy.com/project/badge/Grade/0f7721b7e4314a748c75a04f0a7e0ce3
|
|
46
|
+
[codecov-url]: https://codecov.io/gh/null8626/python-weather
|
|
47
|
+
[codecov-image]: https://codecov.io/gh/null8626/python-weather/graph/badge.svg
|
|
48
|
+
[ko-fi-url]: https://ko-fi.com/null8626
|
|
49
|
+
[ko-fi-image]: https://ko-fi.com/img/githubbutton_sm.svg
|
|
50
|
+
[ko-fi-brief-image]: https://img.shields.io/badge/donations-ko--fi-red?color=ff5e5b&style=flat-square
|
|
51
|
+
|
|
52
|
+
A free and asynchronous weather Python API wrapper made in Python, for Python.
|
|
53
|
+
|
|
54
|
+
## Getting started
|
|
55
|
+
|
|
56
|
+
Run the following command in your terminal:
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
pip install python-weather
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Example
|
|
63
|
+
|
|
64
|
+
For more information, please read the [documentation](https://python-weather.readthedocs.io/en/latest/).
|
|
65
|
+
|
|
66
|
+
```py
|
|
67
|
+
# Import the module.
|
|
68
|
+
import python_weather
|
|
69
|
+
|
|
70
|
+
import asyncio
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def main() -> None:
|
|
74
|
+
|
|
75
|
+
# Declare the client. The measuring unit used defaults to the metric system (celcius, km/h, etc.)
|
|
76
|
+
async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
|
|
77
|
+
|
|
78
|
+
# Fetch a weather forecast from a city.
|
|
79
|
+
weather = await client.get('New York')
|
|
80
|
+
|
|
81
|
+
# Fetch the temperature for today.
|
|
82
|
+
print(weather.temperature)
|
|
83
|
+
|
|
84
|
+
# Fetch weather forecast for upcoming days.
|
|
85
|
+
for daily in weather:
|
|
86
|
+
print(daily)
|
|
87
|
+
|
|
88
|
+
# Each daily forecast has their own hourly forecasts.
|
|
89
|
+
for hourly in daily:
|
|
90
|
+
print(f' --> {hourly!r}')
|
|
91
|
+
|
|
92
|
+
if __name__ == '__main__':
|
|
93
|
+
asyncio.run(main())
|
|
94
|
+
```
|
|
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
|
+
|
|
100
|
+
## Donations
|
|
101
|
+
|
|
102
|
+
If you want to support this project, consider donating! ❤
|
|
103
|
+
|
|
104
|
+
[![ko-fi][ko-fi-image]][ko-fi-url]
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url] [![codacy-badge][codacy-
|
|
1
|
+
# [python-weather][pypi-url] [![pypi][pypi-image]][pypi-url] [![pypi downloads][pypi-downloads-image]][pypi-url] [![codacy-badge][codacy-image]][codacy-url] [![codecov-badge][codecov-image]][codecov-url] [![ko-fi][ko-fi-brief-image]][ko-fi-url]
|
|
2
2
|
|
|
3
|
-
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
4
3
|
[pypi-url]: https://pypi.org/project/python-weather/
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[codacy-url]: https://app.codacy.com/
|
|
8
|
-
[codacy-image]: https://app.codacy.com/
|
|
9
|
-
[
|
|
4
|
+
[pypi-image]: https://img.shields.io/pypi/v/python-weather.svg?style=flat-square
|
|
5
|
+
[pypi-downloads-image]: https://img.shields.io/pypi/dm/python-weather?style=flat-square
|
|
6
|
+
[codacy-url]: https://app.codacy.com/gh/null8626/python-weather/dashboard
|
|
7
|
+
[codacy-image]: https://app.codacy.com/project/badge/Grade/0f7721b7e4314a748c75a04f0a7e0ce3
|
|
8
|
+
[codecov-url]: https://codecov.io/gh/null8626/python-weather
|
|
9
|
+
[codecov-image]: https://codecov.io/gh/null8626/python-weather/graph/badge.svg
|
|
10
10
|
[ko-fi-url]: https://ko-fi.com/null8626
|
|
11
|
+
[ko-fi-image]: https://ko-fi.com/img/githubbutton_sm.svg
|
|
12
|
+
[ko-fi-brief-image]: https://img.shields.io/badge/donations-ko--fi-red?color=ff5e5b&style=flat-square
|
|
11
13
|
|
|
12
14
|
A free and asynchronous weather Python API wrapper made in Python, for Python.
|
|
13
15
|
|
|
@@ -16,7 +18,7 @@ A free and asynchronous weather Python API wrapper made in Python, for Python.
|
|
|
16
18
|
Run the following command in your terminal:
|
|
17
19
|
|
|
18
20
|
```console
|
|
19
|
-
|
|
21
|
+
pip install python-weather
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
## Example
|
|
@@ -28,7 +30,6 @@ For more information, please read the [documentation](https://python-weather.rea
|
|
|
28
30
|
import python_weather
|
|
29
31
|
|
|
30
32
|
import asyncio
|
|
31
|
-
import os
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
async def main() -> None:
|
|
@@ -51,12 +52,6 @@ async def main() -> None:
|
|
|
51
52
|
print(f' --> {hourly!r}')
|
|
52
53
|
|
|
53
54
|
if __name__ == '__main__':
|
|
54
|
-
|
|
55
|
-
# See https://stackoverflow.com/questions/45600579/asyncio-event-loop-is-closed-when-getting-loop
|
|
56
|
-
# for more details.
|
|
57
|
-
if os.name == 'nt':
|
|
58
|
-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
59
|
-
|
|
60
55
|
asyncio.run(main())
|
|
61
56
|
```
|
|
62
57
|
|
|
@@ -3,13 +3,13 @@ requires = ["setuptools"]
|
|
|
3
3
|
|
|
4
4
|
[project]
|
|
5
5
|
name = "python-weather"
|
|
6
|
-
version = "2.1.
|
|
6
|
+
version = "2.1.2"
|
|
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
|
-
keywords = ["weather", "forecast", "weather-
|
|
12
|
-
dependencies = ["aiohttp>=3.
|
|
11
|
+
keywords = ["weather", "forecast", "weather-forecast"]
|
|
12
|
+
dependencies = ["aiohttp>=3.13.3"]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 5 - Production/Stable",
|
|
15
15
|
"Intended Audience :: Education",
|
|
@@ -23,17 +23,16 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Scientific/Engineering :: Astronomy",
|
|
24
24
|
"Topic :: Software Development",
|
|
25
25
|
"Topic :: Software Development :: Libraries",
|
|
26
|
-
"License :: OSI Approved :: MIT License",
|
|
27
26
|
"Programming Language :: Python",
|
|
28
27
|
"Programming Language :: Python :: 3",
|
|
29
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
-
"Programming Language :: Python :: 3.9",
|
|
31
29
|
"Programming Language :: Python :: 3.10",
|
|
32
30
|
"Programming Language :: Python :: 3.11",
|
|
33
31
|
"Programming Language :: Python :: 3.12",
|
|
34
|
-
"Programming Language :: Python :: 3.13"
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14"
|
|
35
34
|
]
|
|
36
|
-
requires-python = ">=3.
|
|
35
|
+
requires-python = ">=3.10"
|
|
37
36
|
|
|
38
37
|
[project.urls]
|
|
39
38
|
Documentation = "https://python-weather.readthedocs.io/en/latest/"
|
|
@@ -41,3 +40,14 @@ Repository = "https://github.com/null8626/python-weather"
|
|
|
41
40
|
Changelog = "https://python-weather.readthedocs.io/en/latest/changelog.html"
|
|
42
41
|
"Donate via GitHub Sponsors" = "https://github.com/sponsors/null8626"
|
|
43
42
|
"Donate via Ko-fi" = "https://ko-fi.com/null8626"
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
minversion = "8.0"
|
|
46
|
+
testpaths = ["tests"]
|
|
47
|
+
|
|
48
|
+
[tool.coverage.report]
|
|
49
|
+
exclude_lines = [
|
|
50
|
+
"^\\s*async def __a\\w+__\\(",
|
|
51
|
+
"# pragma: nocover",
|
|
52
|
+
"TYPE_CHECKING:$"
|
|
53
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# SPDX-FileCopyrightText: 2021-2026 null8626
|
|
3
|
+
|
|
4
|
+
from .enums import HeatIndex, Kind, Locale, Phase, UltraViolet, WindDirection
|
|
5
|
+
from .constants import METRIC, IMPERIAL
|
|
6
|
+
from .errors import Error, RequestError
|
|
7
|
+
from .forecast import Forecast
|
|
8
|
+
from .version import VERSION
|
|
9
|
+
from .client import Client
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__title__ = 'python-weather'
|
|
13
|
+
__author__ = 'null8626'
|
|
14
|
+
__credits__ = (__author__,)
|
|
15
|
+
__maintainer__ = __author__
|
|
16
|
+
__status__ = 'Production'
|
|
17
|
+
__license__ = 'MIT'
|
|
18
|
+
__copyright__ = 'Copyright (c) 2021-2026 null8626'
|
|
19
|
+
__version__ = VERSION
|
|
20
|
+
__all__ = (
|
|
21
|
+
'METRIC',
|
|
22
|
+
'IMPERIAL',
|
|
23
|
+
'Client',
|
|
24
|
+
'Error',
|
|
25
|
+
'Forecast',
|
|
26
|
+
'RequestError',
|
|
27
|
+
'HeatIndex',
|
|
28
|
+
'Kind',
|
|
29
|
+
'Locale',
|
|
30
|
+
'Phase',
|
|
31
|
+
'UltraViolet',
|
|
32
|
+
'VERSION',
|
|
33
|
+
'WindDirection',
|
|
34
|
+
)
|
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Copyright (c) 2021-2025 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
|
-
"""
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# SPDX-FileCopyrightText: 2021-2026 null8626
|
|
24
3
|
|
|
25
4
|
from .enums import WindDirection, Kind, Locale, UltraViolet
|
|
26
5
|
from .constants import _Unit
|
|
@@ -28,7 +7,10 @@ from .errors import Error
|
|
|
28
7
|
|
|
29
8
|
|
|
30
9
|
class CustomizableBase:
|
|
31
|
-
__slots__: tuple[str, ...] =
|
|
10
|
+
__slots__: tuple[str, ...] = '_unit', '_locale'
|
|
11
|
+
|
|
12
|
+
_unit: _Unit
|
|
13
|
+
_locale: Locale
|
|
32
14
|
|
|
33
15
|
def __init__(self, unit: _Unit, locale: Locale):
|
|
34
16
|
self.unit = unit
|
|
@@ -38,7 +20,7 @@ class CustomizableBase:
|
|
|
38
20
|
def unit(self) -> _Unit:
|
|
39
21
|
"""The measuring unit used."""
|
|
40
22
|
|
|
41
|
-
return self.
|
|
23
|
+
return self._unit
|
|
42
24
|
|
|
43
25
|
@unit.setter
|
|
44
26
|
def unit(self, to: _Unit) -> None:
|
|
@@ -53,13 +35,13 @@ class CustomizableBase:
|
|
|
53
35
|
if not isinstance(to, _Unit):
|
|
54
36
|
raise Error('Invalid measuring unit specified!')
|
|
55
37
|
|
|
56
|
-
self.
|
|
38
|
+
self._unit = to
|
|
57
39
|
|
|
58
40
|
@property
|
|
59
41
|
def locale(self) -> Locale:
|
|
60
42
|
"""The localization used."""
|
|
61
43
|
|
|
62
|
-
return self.
|
|
44
|
+
return self._locale
|
|
63
45
|
|
|
64
46
|
@locale.setter
|
|
65
47
|
def locale(self, to: Locale) -> None:
|
|
@@ -75,11 +57,12 @@ class CustomizableBase:
|
|
|
75
57
|
if not isinstance(to, Locale):
|
|
76
58
|
raise Error(f'Expected {to!r} to be a Locale enum')
|
|
77
59
|
|
|
78
|
-
self.
|
|
60
|
+
self._locale = to
|
|
79
61
|
|
|
80
62
|
|
|
81
63
|
class BaseForecast:
|
|
82
64
|
__slots__: tuple[str, ...] = (
|
|
65
|
+
'cloud_cover',
|
|
83
66
|
'ultraviolet',
|
|
84
67
|
'humidity',
|
|
85
68
|
'wind_direction',
|
|
@@ -93,6 +76,9 @@ class BaseForecast:
|
|
|
93
76
|
'description',
|
|
94
77
|
)
|
|
95
78
|
|
|
79
|
+
cloud_cover: int
|
|
80
|
+
"""The cloud cover value in percent."""
|
|
81
|
+
|
|
96
82
|
ultraviolet: UltraViolet
|
|
97
83
|
"""The ultra-violet index."""
|
|
98
84
|
|
|
@@ -133,6 +119,7 @@ class BaseForecast:
|
|
|
133
119
|
else json[f'lang_{locale.value}'][0]['value']
|
|
134
120
|
)
|
|
135
121
|
|
|
122
|
+
self.cloud_cover = int(json['cloudcover'])
|
|
136
123
|
self.ultraviolet = UltraViolet._new(int(json['uvIndex']))
|
|
137
124
|
self.humidity = int(json['humidity'])
|
|
138
125
|
self.wind_direction = WindDirection._new(
|
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Copyright (c) 2021-2025 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
|
-
"""
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# SPDX-FileCopyrightText: 2021-2026 null8626
|
|
24
3
|
|
|
25
4
|
from aiohttp import ClientSession, ClientTimeout, ClientResponseError, TCPConnector
|
|
26
5
|
from urllib.parse import quote_plus
|
|
27
|
-
from typing import Optional
|
|
28
6
|
from asyncio import sleep
|
|
29
7
|
|
|
30
8
|
from .errors import Error, RequestError
|
|
@@ -39,56 +17,82 @@ class Client(CustomizableBase):
|
|
|
39
17
|
"""
|
|
40
18
|
Interact with the API's endpoints.
|
|
41
19
|
|
|
20
|
+
Examples:
|
|
21
|
+
|
|
22
|
+
.. code-block:: python
|
|
23
|
+
|
|
24
|
+
# Explicit cleanup
|
|
25
|
+
client = python_weather.Client(unit=python_weather.IMPERIAL)
|
|
26
|
+
|
|
27
|
+
# ...
|
|
28
|
+
|
|
29
|
+
await client.close()
|
|
30
|
+
|
|
31
|
+
# Implicit cleanup
|
|
32
|
+
async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
|
|
33
|
+
# ...
|
|
34
|
+
|
|
42
35
|
:param unit: Whether to use the metric or imperial/customary system (:data:`~.constants.IMPERIAL`). Defaults to :data:`~.constants.METRIC`.
|
|
43
36
|
:type unit: ``_Unit``
|
|
44
37
|
:param locale: Whether to use a different locale/language as the description for the returned forecast. Defaults to :attr:`.Locale.ENGLISH`.
|
|
45
38
|
:type locale: :class:`.Locale`
|
|
46
39
|
:param session: Whether to use an existing :class:`~aiohttp.ClientSession` for requesting or not. Defaults to :py:obj:`None` (creates a new one instead).
|
|
47
|
-
:type session:
|
|
48
|
-
:param max_retries: Maximum amount of retries upon request failure before raising a :class:`.RequestError`.
|
|
49
|
-
|
|
40
|
+
:type session: :class:`~aiohttp.ClientSession` | :py:obj:`None`
|
|
41
|
+
:param max_retries: Maximum amount of retries upon request failure before raising a :class:`.RequestError`.
|
|
42
|
+
Use ``-1`` to disable (NOT recommended). Defaults to 3 retries.
|
|
43
|
+
:type max_retries: :class:`int` | :py:obj:`None`
|
|
50
44
|
|
|
51
45
|
:exception Error: ``unit`` is not :data:`~.constants.METRIC` or :data:`~.constants.IMPERIAL` or ``locale`` is not a part of the :class:`.Locale` enum.
|
|
52
46
|
"""
|
|
53
47
|
|
|
54
|
-
__slots__: tuple[str, ...] =
|
|
48
|
+
__slots__: tuple[str, ...] = '__own_session', '__session', '_max_retries'
|
|
49
|
+
|
|
50
|
+
__own_session: bool
|
|
51
|
+
__session: ClientSession
|
|
52
|
+
_max_retries: int
|
|
55
53
|
|
|
56
54
|
def __init__(
|
|
57
55
|
self,
|
|
58
56
|
*,
|
|
59
57
|
unit: _Unit = METRIC,
|
|
60
58
|
locale: Locale = Locale.ENGLISH,
|
|
61
|
-
session:
|
|
62
|
-
max_retries:
|
|
59
|
+
session: ClientSession | None = None,
|
|
60
|
+
max_retries: int | None = None,
|
|
63
61
|
):
|
|
64
62
|
super().__init__(unit, locale)
|
|
65
63
|
|
|
66
64
|
self.__own_session = session is None
|
|
67
65
|
self.__session = session or ClientSession(
|
|
68
66
|
timeout=ClientTimeout(total=5000.0),
|
|
69
|
-
connector=TCPConnector(
|
|
67
|
+
connector=TCPConnector(ssl=False),
|
|
70
68
|
)
|
|
71
|
-
self.
|
|
69
|
+
self._max_retries = max_retries or 3
|
|
72
70
|
|
|
73
71
|
def __repr__(self) -> str:
|
|
74
|
-
return f'<{__class__.__name__} {self.__session!r}>'
|
|
72
|
+
return f'<{__class__.__module__}.{__class__.__name__} {self.__session!r}>'
|
|
75
73
|
|
|
76
74
|
async def get(
|
|
77
75
|
self,
|
|
78
76
|
location: str,
|
|
79
77
|
*,
|
|
80
|
-
unit:
|
|
81
|
-
locale:
|
|
78
|
+
unit: _Unit | None = None,
|
|
79
|
+
locale: Locale | None = None,
|
|
82
80
|
) -> Forecast:
|
|
83
81
|
"""
|
|
84
82
|
Fetches a weather forecast for a specific location.
|
|
85
83
|
|
|
84
|
+
Example:
|
|
85
|
+
|
|
86
|
+
.. code-block:: python
|
|
87
|
+
|
|
88
|
+
weather = await client.get('New York')
|
|
89
|
+
|
|
86
90
|
:param location: The requested location.
|
|
87
91
|
:type location: :py:class:`str`
|
|
88
92
|
:param unit: Overrides the unit used.
|
|
89
|
-
:type unit:
|
|
93
|
+
:type unit: ``_Unit`` | :py:obj:`None`
|
|
90
94
|
:param locale: Overrides the locale used.
|
|
91
|
-
:type locale:
|
|
95
|
+
:type locale: :class:`.Locale` | :py:obj:`None`
|
|
92
96
|
|
|
93
97
|
:exception TypeError: ``location`` is not a :py:class:`str` or is empty.
|
|
94
98
|
:exception Error: The client is already closed.
|
|
@@ -105,14 +109,16 @@ class Client(CustomizableBase):
|
|
|
105
109
|
raise TypeError(f'Expected a proper location str, got {location!r}')
|
|
106
110
|
|
|
107
111
|
if not isinstance(unit, _Unit):
|
|
108
|
-
unit = self.
|
|
112
|
+
unit = self._unit
|
|
109
113
|
|
|
110
114
|
if not isinstance(locale, Locale):
|
|
111
|
-
locale = self.
|
|
115
|
+
locale = self._locale
|
|
112
116
|
|
|
113
117
|
subdomain = f'{locale.value}.' if locale != Locale.ENGLISH else ''
|
|
114
118
|
attempts = 0
|
|
119
|
+
|
|
115
120
|
status = None
|
|
121
|
+
reason = None
|
|
116
122
|
|
|
117
123
|
while True:
|
|
118
124
|
try:
|
|
@@ -124,18 +130,28 @@ class Client(CustomizableBase):
|
|
|
124
130
|
},
|
|
125
131
|
) as resp:
|
|
126
132
|
status = resp.status
|
|
133
|
+
reason = resp.reason
|
|
134
|
+
|
|
127
135
|
resp.raise_for_status()
|
|
128
136
|
|
|
129
137
|
return Forecast(await resp.json(), unit, locale)
|
|
130
138
|
except ClientResponseError:
|
|
131
|
-
if attempts == self.
|
|
132
|
-
raise RequestError(status) from None
|
|
139
|
+
if attempts == self._max_retries:
|
|
140
|
+
raise RequestError(status, reason) from None
|
|
133
141
|
|
|
134
142
|
await sleep(0.5 * (2**attempts))
|
|
135
143
|
attempts += 1
|
|
136
144
|
|
|
137
145
|
async def close(self) -> None:
|
|
138
|
-
"""
|
|
146
|
+
"""
|
|
147
|
+
Closes the :class:`.Client` object.
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
|
|
151
|
+
.. code-block:: python
|
|
152
|
+
|
|
153
|
+
await client.close()
|
|
154
|
+
"""
|
|
139
155
|
|
|
140
156
|
if self.__own_session and not self.__session.closed:
|
|
141
157
|
await self.__session.close()
|