Add config.update support for setters (#2354)
This commit is contained in:
parent
dc3ccba527
commit
34d1dee407
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
|
@ -2,9 +2,13 @@ name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches:
|
||||||
|
- main
|
||||||
|
- "*LTS"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches:
|
||||||
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '25 16 * * 0'
|
- cron: '25 16 * * 0'
|
||||||
|
|
1
.github/workflows/coverage.yml
vendored
1
.github/workflows/coverage.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
tags:
|
tags:
|
||||||
- "!*" # Do not execute on tags
|
- "!*" # Do not execute on tags
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
1
.github/workflows/pr-bandit.yml
vendored
1
.github/workflows/pr-bandit.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-docs.yml
vendored
1
.github/workflows/pr-docs.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-linter.yml
vendored
1
.github/workflows/pr-linter.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-python310.yml
vendored
1
.github/workflows/pr-python310.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-python37.yml
vendored
1
.github/workflows/pr-python37.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-python38.yml
vendored
1
.github/workflows/pr-python38.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-python39.yml
vendored
1
.github/workflows/pr-python39.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-type-check.yml
vendored
1
.github/workflows/pr-type-check.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/pr-windows.yml
vendored
1
.github/workflows/pr-windows.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- "*LTS"
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
@ -124,22 +124,27 @@ class Config(dict, metaclass=DescriptorMeta):
|
||||||
raise AttributeError(f"Config has no '{ke.args[0]}'")
|
raise AttributeError(f"Config has no '{ke.args[0]}'")
|
||||||
|
|
||||||
def __setattr__(self, attr, value) -> None:
|
def __setattr__(self, attr, value) -> None:
|
||||||
if attr in self.__class__.__setters__:
|
|
||||||
try:
|
|
||||||
super().__setattr__(attr, value)
|
|
||||||
except AttributeError:
|
|
||||||
...
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
self.update({attr: value})
|
self.update({attr: value})
|
||||||
|
|
||||||
def __setitem__(self, attr, value) -> None:
|
def __setitem__(self, attr, value) -> None:
|
||||||
self.update({attr: value})
|
self.update({attr: value})
|
||||||
|
|
||||||
def update(self, *other, **kwargs) -> None:
|
def update(self, *other, **kwargs) -> None:
|
||||||
other_mapping = {k: v for item in other for k, v in dict(item).items()}
|
kwargs.update({k: v for item in other for k, v in dict(item).items()})
|
||||||
super().update(*other, **kwargs)
|
setters: Dict[str, Any] = {
|
||||||
for attr, value in {**other_mapping, **kwargs}.items():
|
k: kwargs.pop(k)
|
||||||
|
for k in {**kwargs}.keys()
|
||||||
|
if k in self.__class__.__setters__
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in setters.items():
|
||||||
|
try:
|
||||||
|
super().__setattr__(key, value)
|
||||||
|
except AttributeError:
|
||||||
|
...
|
||||||
|
|
||||||
|
super().update(**kwargs)
|
||||||
|
for attr, value in {**setters, **kwargs}.items():
|
||||||
self._post_set(attr, value)
|
self._post_set(attr, value)
|
||||||
|
|
||||||
def _post_set(self, attr, value) -> None:
|
def _post_set(self, attr, value) -> None:
|
||||||
|
|
|
@ -5,7 +5,7 @@ from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock, call
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -385,5 +385,24 @@ def test_config_set_methods(app, monkeypatch):
|
||||||
post_set.assert_called_once_with("FOO", 5)
|
post_set.assert_called_once_with("FOO", 5)
|
||||||
post_set.reset_mock()
|
post_set.reset_mock()
|
||||||
|
|
||||||
app.config.update_config({"FOO": 6})
|
app.config.update({"FOO": 6}, {"BAR": 7})
|
||||||
post_set.assert_called_once_with("FOO", 6)
|
post_set.assert_has_calls(
|
||||||
|
calls=[
|
||||||
|
call("FOO", 6),
|
||||||
|
call("BAR", 7),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update({"FOO": 8}, BAR=9)
|
||||||
|
post_set.assert_has_calls(
|
||||||
|
calls=[
|
||||||
|
call("FOO", 8),
|
||||||
|
call("BAR", 9),
|
||||||
|
],
|
||||||
|
any_order=True,
|
||||||
|
)
|
||||||
|
post_set.reset_mock()
|
||||||
|
|
||||||
|
app.config.update_config({"FOO": 10})
|
||||||
|
post_set.assert_called_once_with("FOO", 10)
|
||||||
|
|
|
@ -334,6 +334,22 @@ def test_config_fallback_before_and_after_startup(app):
|
||||||
assert response.content_type == "text/plain; charset=utf-8"
|
assert response.content_type == "text/plain; charset=utf-8"
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_fallback_using_update_dict(app):
|
||||||
|
app.config.update({"FALLBACK_ERROR_FORMAT": "text"})
|
||||||
|
|
||||||
|
_, response = app.test_client.get("/error")
|
||||||
|
assert response.status == 500
|
||||||
|
assert response.content_type == "text/plain; charset=utf-8"
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_fallback_using_update_kwarg(app):
|
||||||
|
app.config.update(FALLBACK_ERROR_FORMAT="text")
|
||||||
|
|
||||||
|
_, response = app.test_client.get("/error")
|
||||||
|
assert response.status == 500
|
||||||
|
assert response.content_type == "text/plain; charset=utf-8"
|
||||||
|
|
||||||
|
|
||||||
def test_config_fallback_bad_value(app):
|
def test_config_fallback_bad_value(app):
|
||||||
message = "Unknown format: fake"
|
message = "Unknown format: fake"
|
||||||
with pytest.raises(SanicException, match=message):
|
with pytest.raises(SanicException, match=message):
|
||||||
|
|
|
@ -62,19 +62,15 @@ def test_streaming_body_requests(app):
|
||||||
|
|
||||||
data = ["hello", "world"]
|
data = ["hello", "world"]
|
||||||
|
|
||||||
class Data(AsyncByteStream):
|
|
||||||
def __init__(self, data):
|
|
||||||
self.data = data
|
|
||||||
|
|
||||||
async def __aiter__(self):
|
|
||||||
for value in self.data:
|
|
||||||
yield value.encode("utf-8")
|
|
||||||
|
|
||||||
client = ReusableClient(app, port=1234)
|
client = ReusableClient(app, port=1234)
|
||||||
|
|
||||||
|
async def stream(data):
|
||||||
|
for value in data:
|
||||||
|
yield value.encode("utf-8")
|
||||||
|
|
||||||
with client:
|
with client:
|
||||||
_, response1 = client.post("/", data=Data(data))
|
_, response1 = client.post("/", data=stream(data))
|
||||||
_, response2 = client.post("/", data=Data(data))
|
_, response2 = client.post("/", data=stream(data))
|
||||||
|
|
||||||
assert response1.status == response2.status == 200
|
assert response1.status == response2.status == 200
|
||||||
assert response1.json["data"] == response2.json["data"] == data
|
assert response1.json["data"] == response2.json["data"] == data
|
||||||
|
|
|
@ -4,8 +4,8 @@ from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic.server import loop
|
|
||||||
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
|
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
|
||||||
|
from sanic.server import loop
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user