Add better handling of deprecated configs (#20565)

* Add better handling of deprecated configs

* Embed the call to has_at_most_one_key in deprecated

* Add tests for checking the deprecated logs

* Add thoroughly documented tests

* Always check has_at_most_one_key

* Fix typing

* Move logging helpers to homea new logging helper

* Lint

* Rename to KeywordMessage instead of BraceMessage

* Remove unneeded KeywordStyleAdapter

* Lint

* Use dict directly rather than dict.keys() when creating set

* Patch the version in unit tests, update logging and use parse_version

* Re-add KeywordStyleAdapter and fix tests

* Lint

* Lint
This commit is contained in:
Rohan Kapoor 2019-02-08 02:14:50 -08:00 committed by Martin Hjelmare
parent ee3631e93e
commit d5fad33599
5 changed files with 487 additions and 40 deletions

View file

@ -5,6 +5,7 @@ import os
from socket import _GLOBAL_DEFAULT_TIMEOUT
from unittest.mock import Mock, patch
import homeassistant
import pytest
import voluptuous as vol
@ -275,7 +276,6 @@ def test_time_period():
{}, {'wrong_key': -10}
)
for value in options:
with pytest.raises(vol.MultipleInvalid):
schema(value)
@ -489,26 +489,323 @@ def test_datetime():
schema('2016-11-23T18:59:08')
def test_deprecated(caplog):
"""Test deprecation log."""
schema = vol.Schema({
@pytest.fixture
def schema():
"""Create a schema used for testing deprecation."""
return vol.Schema({
'venus': cv.boolean,
'mars': cv.boolean
'mars': cv.boolean,
'jupiter': cv.boolean
})
@pytest.fixture
def version(monkeypatch):
"""Patch the version used for testing to 0.5.0."""
monkeypatch.setattr(homeassistant.const, '__version__', '0.5.0')
def test_deprecated_with_no_optionals(caplog, schema):
"""
Test deprecation behaves correctly when optional params are None.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema without changing any values
- No warning or difference in output if key is not provided
"""
deprecated_schema = vol.All(
cv.deprecated('mars'),
schema
)
deprecated_schema({'venus': True})
# pylint: disable=len-as-condition
assert len(caplog.records) == 0
deprecated_schema({'mars': True})
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert caplog.records[0].name == __name__
assert ("The 'mars' option (with value 'True') is deprecated, "
"please remove it from your configuration.") in caplog.text
"please remove it from your configuration") in caplog.text
assert test_data == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
def test_deprecated_with_replacement_key(caplog, schema):
"""
Test deprecation behaves correctly when only a replacement key is provided.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema moving the value from key to replacement_key
- Processes schema changing nothing if only replacement_key provided
- No warning if only replacement_key provided
- No warning or difference in output if neither key nor
replacement_key are provided
"""
deprecated_schema = vol.All(
cv.deprecated('mars', replacement_key='jupiter'),
schema
)
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'") in caplog.text
assert {'jupiter': True} == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'jupiter': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
def test_deprecated_with_invalidation_version(caplog, schema, version):
"""
Test deprecation behaves correctly with only an invalidation_version.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema without changing any values
- No warning or difference in output if key is not provided
- Once the invalidation_version is crossed, raises vol.Invalid if key
is detected
"""
deprecated_schema = vol.All(
cv.deprecated('mars', invalidation_version='1.0.0'),
schema
)
message = ("The 'mars' option (with value 'True') is deprecated, "
"please remove it from your configuration. "
"This option will become invalid in version 1.0.0")
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert message in caplog.text
assert test_data == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'venus': False}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
invalidated_schema = vol.All(
cv.deprecated('mars', invalidation_version='0.1.0'),
schema
)
test_data = {'mars': True}
with pytest.raises(vol.MultipleInvalid) as exc_info:
invalidated_schema(test_data)
assert ("The 'mars' option (with value 'True') is deprecated, "
"please remove it from your configuration. This option will "
"become invalid in version 0.1.0") == str(exc_info.value)
def test_deprecated_with_replacement_key_and_invalidation_version(
caplog, schema, version
):
"""
Test deprecation behaves with a replacement key & invalidation_version.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema moving the value from key to replacement_key
- Processes schema changing nothing if only replacement_key provided
- No warning if only replacement_key provided
- No warning or difference in output if neither key nor
replacement_key are provided
- Once the invalidation_version is crossed, raises vol.Invalid if key
is detected
"""
deprecated_schema = vol.All(
cv.deprecated(
'mars', replacement_key='jupiter', invalidation_version='1.0.0'
),
schema
)
warning = ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'. This option will become "
"invalid in version 1.0.0")
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert warning in caplog.text
assert {'jupiter': True} == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'jupiter': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
invalidated_schema = vol.All(
cv.deprecated(
'mars', replacement_key='jupiter', invalidation_version='0.1.0'
),
schema
)
test_data = {'mars': True}
with pytest.raises(vol.MultipleInvalid) as exc_info:
invalidated_schema(test_data)
assert ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'. This option will become "
"invalid in version 0.1.0") == str(exc_info.value)
def test_deprecated_with_default(caplog, schema):
"""
Test deprecation behaves correctly with a default value.
This is likely a scenario that would never occur.
Expected behavior:
- Behaves identically as when the default value was not present
"""
deprecated_schema = vol.All(
cv.deprecated('mars', default=False),
schema
)
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert caplog.records[0].name == __name__
assert ("The 'mars' option (with value 'True') is deprecated, "
"please remove it from your configuration") in caplog.text
assert test_data == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
def test_deprecated_with_replacement_key_and_default(caplog, schema):
"""
Test deprecation behaves correctly when only a replacement key is provided.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema moving the value from key to replacement_key
- Processes schema changing nothing if only replacement_key provided
- No warning if only replacement_key provided
- No warning if neither key nor replacement_key are provided
- Adds replacement_key with default value in this case
"""
deprecated_schema = vol.All(
cv.deprecated('mars', replacement_key='jupiter', default=False),
schema
)
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'") in caplog.text
assert {'jupiter': True} == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'jupiter': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert {'venus': True, 'jupiter': False} == output
def test_deprecated_with_replacement_key_invalidation_version_default(
caplog, schema, version
):
"""
Test deprecation with a replacement key, invalidation_version & default.
Expected behavior:
- Outputs the appropriate deprecation warning if key is detected
- Processes schema moving the value from key to replacement_key
- Processes schema changing nothing if only replacement_key provided
- No warning if only replacement_key provided
- No warning if neither key nor replacement_key are provided
- Adds replacement_key with default value in this case
- Once the invalidation_version is crossed, raises vol.Invalid if key
is detected
"""
deprecated_schema = vol.All(
cv.deprecated(
'mars', replacement_key='jupiter', invalidation_version='1.0.0',
default=False
),
schema
)
test_data = {'mars': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 1
assert ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'. This option will become "
"invalid in version 1.0.0") in caplog.text
assert {'jupiter': True} == output
caplog.clear()
assert len(caplog.records) == 0
test_data = {'jupiter': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert test_data == output
test_data = {'venus': True}
output = deprecated_schema(test_data.copy())
assert len(caplog.records) == 0
assert {'venus': True, 'jupiter': False} == output
invalidated_schema = vol.All(
cv.deprecated(
'mars', replacement_key='jupiter', invalidation_version='0.1.0'
),
schema
)
test_data = {'mars': True}
with pytest.raises(vol.MultipleInvalid) as exc_info:
invalidated_schema(test_data)
assert ("The 'mars' option (with value 'True') is deprecated, "
"please replace it with 'jupiter'. This option will become "
"invalid in version 0.1.0") == str(exc_info.value)
def test_key_dependency():
@ -530,6 +827,18 @@ def test_key_dependency():
schema(value)
def test_has_at_most_one_key():
"""Test has_at_most_one_key validator."""
schema = vol.Schema(cv.has_at_most_one_key('beer', 'soda'))
for value in (None, [], {'beer': None, 'soda': None}):
with pytest.raises(vol.MultipleInvalid):
schema(value)
for value in ({}, {'beer': None}, {'soda': None}):
schema(value)
def test_has_at_least_one_key():
"""Test has_at_least_one_key validator."""
schema = vol.Schema(cv.has_at_least_one_key('beer', 'soda'))
@ -582,7 +891,7 @@ def test_matches_regex():
schema(" nrtd ")
test_str = "This is a test including uiae."
assert(schema(test_str) == test_str)
assert (schema(test_str) == test_str)
def test_is_regex():