Skip to content

Commit b8803b4

Browse files
authored
Merge pull request #27 from endlessm/python-3.11
Use Python 3.11
2 parents a61cc09 + 5879080 commit b8803b4

14 files changed

Lines changed: 1175 additions & 611 deletions

File tree

.github/workflows/tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ jobs:
5151
name: image
5252
- name: Load Docker Image
5353
run: docker load < docker-image.tar
54-
- name: Lint and Type Checking
54+
- name: Lint
5555
run: docker run --rm --entrypoint="" amp pipenv run lint
56+
- name: Type Checking
57+
run: docker run --rm --entrypoint="" amp pipenv run type-check
5658

5759
push:
5860
name: Upload the Docker image to Docker Hub

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ fixing your issue as quickly as possible.
2828

2929
The tools required to work on the Azafea metrics proxy are the following:
3030

31-
* Python >= 3.7, with Pip
32-
* [pipenv](https://docs.pipenv.org/)
31+
* Python >= 3.11, with Pip
32+
* [Pipenv](https://docs.pipenv.org/) >= 2023.10.24
3333

3434
Pipenv is not strictly mandatory to contribute to the Azafea metrics proxy, you
3535
can use any way you prefer to manage the dependencies. We do recommend using it
@@ -69,10 +69,11 @@ community.
6969
We also use type checking with [mypy](http://www.mypy-lang.org/), which
7070
prevents a lot of problems inherent to dynamically typed languages like Python.
7171

72-
Both are run automatically with the following command:
72+
Both are run with the following commands:
7373

7474
```
7575
$ pipenv run lint
76+
$ pipenv run type-check
7677
```
7778

7879

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.7-alpine
1+
FROM python:3.11-alpine
22

33
RUN pip install --no-cache-dir pipenv template && \
44
apk add --update --no-cache git build-base

Pipfile

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1+
[pipenv]
2+
sort_pipfile = true
3+
4+
[requires]
5+
python_version = "3.11"
6+
17
[dev-packages]
8+
flake8 = "*"
29
flake8-bugbear = "*"
310
freezegun = "*"
411
mypy = ">=0.720"
512
pytest = "*"
613
pytest-aiohttp = "*"
14+
pytest-asyncio = "*"
715
pytest-cov = "*"
8-
pytest-flake8 = "*"
9-
pytest-mypy = ">=0.3.3"
16+
types-toml = "*"
1017

1118
[packages]
1219
aiohttp = ">= 3.5.0"
1320
aioredis = "~= 1.3"
14-
pydantic = ">= 1.1"
21+
pydantic = "~= 2.7"
1522
toml = "*"
1623

1724
[scripts]
18-
lint = "py.test --flake8 --mypy -m 'flake8 or mypy'"
25+
lint = "flake8"
1926
proxy = "python -m azafea_metrics_proxy"
2027
test = "py.test --cov=azafea_metrics_proxy --cov-fail-under=99 --no-cov-on-fail -m 'not integration'"
2128
test-all = "py.test --cov=azafea_metrics_proxy --cov-fail-under=99 --no-cov-on-fail"
29+
type-check = "mypy"

Pipfile.lock

Lines changed: 1090 additions & 562 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

azafea_metrics_proxy/config/__init__.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
import dataclasses
2424
import logging
2525
import os
26-
from typing import Any, Dict, List, MutableMapping
26+
from typing import Any, List, MutableMapping
2727

28-
from pydantic.class_validators import validator
28+
from pydantic import field_validator, ValidationError
2929
from pydantic.dataclasses import dataclass
30-
from pydantic.error_wrappers import ValidationError
30+
from pydantic_core import ErrorDetails
3131

3232
import toml
3333

@@ -40,18 +40,32 @@
4040

4141

4242
class InvalidConfigurationError(Exception):
43-
def __init__(self, errors: List[Dict[str, Any]]) -> None:
43+
def __init__(self, errors: List[ErrorDetails]) -> None:
4444
self.errors = errors
4545

4646
def __str__(self) -> str:
4747
msg = ['Invalid configuration:']
4848

4949
for e in self.errors:
50-
loc = e['loc']
51-
msg.append(f"* {'.'.join(loc)}: {e['msg']}")
50+
loc = self._loc_str(e)
51+
msg.append(f"* {loc}: {e['msg']}")
5252

5353
return '\n'.join(msg)
5454

55+
# The loc field in ErrorDetails is a tuple of strings and ints.
56+
# https://docs.pydantic.dev/latest/errors/errors/#customize-error-messages
57+
@staticmethod
58+
def _loc_str(details: ErrorDetails) -> str:
59+
path = ''
60+
for i, element in enumerate(details['loc']):
61+
if isinstance(element, str):
62+
if i > 0:
63+
path += '.'
64+
path += element
65+
else: # pragma: no cover (our config does not use lists)
66+
path += f'[{element}]'
67+
return path
68+
5569

5670
class NoSuchConfigurationError(AttributeError):
5771
pass
@@ -66,7 +80,7 @@ def __getattr__(self, name: str) -> Any:
6680
class Main(_Base):
6781
verbose: bool = False
6882

69-
@validator('verbose', pre=True)
83+
@field_validator('verbose', mode='before')
7084
def verbose_is_boolean(cls, value: Any) -> bool:
7185
return is_boolean(value)
7286

@@ -78,11 +92,11 @@ class Redis(_Base):
7892
password: str = DEFAULT_PASSWORD
7993
ssl: bool = False
8094

81-
@validator('host', pre=True)
95+
@field_validator('host', mode='before')
8296
def host_is_non_empty_string(cls, value: Any) -> str:
8397
return is_non_empty_string(value)
8498

85-
@validator('port', pre=True)
99+
@field_validator('port', mode='before')
86100
def port_is_strictly_positive_integer(cls, value: Any) -> int:
87101
return is_strictly_positive_integer(value)
88102

@@ -92,7 +106,7 @@ class Config(_Base):
92106
main: Main = dataclasses.field(default_factory=Main)
93107
redis: Redis = dataclasses.field(default_factory=Redis)
94108

95-
def __post_init_post_parse__(self) -> None:
109+
def __post_init__(self) -> None:
96110
self.warn_about_default_passwords()
97111

98112
@classmethod

azafea_metrics_proxy/config/_validators.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525

2626
def is_boolean(value: Any) -> bool:
2727
if not isinstance(value, bool):
28-
raise TypeError(f'{value!r} is not a boolean')
28+
raise ValueError(f'{value!r} is not a boolean')
2929

3030
return value
3131

3232

3333
def is_non_empty_string(value: Any) -> str:
3434
if not isinstance(value, str):
35-
raise TypeError(f'{value!r} is not a string')
35+
raise ValueError(f'{value!r} is not a string')
3636

3737
if not value:
3838
raise ValueError(f'{value!r} is empty')
@@ -42,7 +42,7 @@ def is_non_empty_string(value: Any) -> str:
4242

4343
def is_strictly_positive_integer(value: Any) -> int:
4444
if type(value) is not int:
45-
raise TypeError(f'{value!r} is not an integer')
45+
raise ValueError(f'{value!r} is not an integer')
4646

4747
if value < 1:
4848
raise ValueError(f'{value!r} is not a strictly positive integer')

azafea_metrics_proxy/tests/conftest.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@
2828

2929
def pytest_collection_modifyitems(items):
3030
for item in items:
31-
markers = [m for m in item.own_markers if m.name in ('flake8', 'mypy')]
32-
33-
if markers:
34-
continue
35-
3631
if item.nodeid.startswith('azafea_metrics_proxy/tests/integration/'):
3732
item.add_marker(pytest.mark.integration)
3833

azafea_metrics_proxy/tests/integration/conftest.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
# along with azafea-metrics-proxy. If not, see <http://www.gnu.org/licenses/>.
1717

1818

19-
from aioredis import Redis
20-
2119
import pytest
2220

2321
from azafea_metrics_proxy.app import get_app
@@ -28,7 +26,7 @@ async def app(make_config):
2826
config = make_config({})
2927

3028
app = await get_app(config)
31-
redis: Redis = app['redis']
29+
redis = app['redis']
3230

3331
async def clear_queues():
3432
queues = await redis.keys('*')

azafea_metrics_proxy/tests/test_cli.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ def test_print_invalid_config(capfd, make_config_file):
5151
azafea_metrics_proxy.cli.run_command('-c', str(config_file), 'print-config')
5252

5353
capture = capfd.readouterr()
54-
assert "Invalid configuration:\n* main.verbose: 'blah' is not a boolean" in capture.err
54+
assert (
55+
"Invalid configuration:\n"
56+
"* main.verbose: Value error, 'blah' is not a boolean"
57+
) in capture.err
5558

5659

5760
def test_run(capfd, monkeypatch, make_config_file):
@@ -79,7 +82,10 @@ def test_run_invalid_config(capfd, make_config_file):
7982
azafea_metrics_proxy.cli.run_command('-c', str(config_file), 'run')
8083

8184
capture = capfd.readouterr()
82-
assert "Invalid configuration:\n* main.verbose: 'blah' is not a boolean" in capture.err
85+
assert (
86+
"Invalid configuration:\n"
87+
"* main.verbose: Value error, 'blah' is not a boolean"
88+
) in capture.err
8389

8490

8591
def test_run_redis_invalid_host(capfd, make_config_file):

0 commit comments

Comments
 (0)