Skip to content

Commit

Permalink
Add config.update support for setters (#2354)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahopkins committed Jan 6, 2022
1 parent dc3ccba commit 34d1dee
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 26 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/codeql-analysis.yml
Expand Up @@ -2,9 +2,13 @@ name: "CodeQL"

on:
push:
branches: [ main ]
branches:
- main
- "*LTS"
pull_request:
branches: [ main ]
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]
schedule:
- cron: '25 16 * * 0'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/coverage.yml
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main
- "*LTS"
tags:
- "!*" # Do not execute on tags
pull_request:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-bandit.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-docs.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-linter.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-python310.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-python37.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-python38.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-python39.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-type-check.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-windows.yml
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
branches:
- main
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review]

jobs:
Expand Down
25 changes: 15 additions & 10 deletions sanic/config.py
Expand Up @@ -124,22 +124,27 @@ def __getattr__(self, attr):
raise AttributeError(f"Config has no '{ke.args[0]}'")

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})

def __setitem__(self, attr, value) -> None:
self.update({attr: value})

def update(self, *other, **kwargs) -> None:
other_mapping = {k: v for item in other for k, v in dict(item).items()}
super().update(*other, **kwargs)
for attr, value in {**other_mapping, **kwargs}.items():
kwargs.update({k: v for item in other for k, v in dict(item).items()})
setters: Dict[str, Any] = {
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)

def _post_set(self, attr, value) -> None:
Expand Down
25 changes: 22 additions & 3 deletions tests/test_config.py
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path
from tempfile import TemporaryDirectory
from textwrap import dedent
from unittest.mock import Mock
from unittest.mock import Mock, call

import pytest

Expand Down Expand Up @@ -385,5 +385,24 @@ def test_config_set_methods(app, monkeypatch):
post_set.assert_called_once_with("FOO", 5)
post_set.reset_mock()

app.config.update_config({"FOO": 6})
post_set.assert_called_once_with("FOO", 6)
app.config.update({"FOO": 6}, {"BAR": 7})
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)
16 changes: 16 additions & 0 deletions tests/test_errorpages.py
Expand Up @@ -334,6 +334,22 @@ async def start(app, _):
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):
message = "Unknown format: fake"
with pytest.raises(SanicException, match=message):
Expand Down
16 changes: 6 additions & 10 deletions tests/test_pipelining.py
Expand Up @@ -62,19 +62,15 @@ async def handler(request):

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)

async def stream(data):
for value in data:
yield value.encode("utf-8")

with client:
_, response1 = client.post("/", data=Data(data))
_, response2 = client.post("/", data=Data(data))
_, response1 = client.post("/", data=stream(data))
_, response2 = client.post("/", data=stream(data))

assert response1.status == response2.status == 200
assert response1.json["data"] == response2.json["data"] == data
Expand Down
2 changes: 1 addition & 1 deletion tests/test_server_loop.py
Expand Up @@ -4,8 +4,8 @@

import pytest

from sanic.server import loop
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
from sanic.server import loop


@pytest.mark.skipif(
Expand Down

0 comments on commit 34d1dee

Please sign in to comment.