Skip to content

Commit

Permalink
Add list of 'implemented' feature flags and warning
Browse files Browse the repository at this point in the history
  • Loading branch information
cottsay committed May 20, 2024
1 parent 5c309fa commit 6566fa5
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 10 deletions.
4 changes: 4 additions & 0 deletions colcon_core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from colcon_core.argument_parser import decorate_argument_parser # noqa: E402 E501 I100 I202
from colcon_core.argument_parser import SuppressUsageOutput # noqa: E402
from colcon_core.extension_point import load_extension_points # noqa: E402
from colcon_core.feature_flags import check_implemented_flags # noqa: E402
from colcon_core.location import create_log_path # noqa: E402
from colcon_core.location import get_log_path # noqa: E402
from colcon_core.location import set_default_config_path # noqa: E402
Expand Down Expand Up @@ -135,6 +136,9 @@ def _main(*, command_name, argv):
'Command line arguments: {argv}'
.format(argv=argv if argv is not None else sys.argv))

# warn about any specified feature flags that are already implemented
check_implemented_flags()

# set default locations for config files, for searchability: COLCON_HOME
set_default_config_path(
path=(Path('~') / f'.{command_name}').expanduser(),
Expand Down
22 changes: 21 additions & 1 deletion colcon_core/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@

_REPORTED_USES = set()

IMPLEMENTED_FLAGS = set()


def check_implemented_flags():
"""Check for and warn about flags which have been implemented."""
implemented = IMPLEMENTED_FLAGS.intersection(get_feature_flags())
if implemented:
logger.warning(
'The following feature flags have been implemented and should no '
'longer be specified in '
f'{FEATURE_FLAGS_ENVIRONMENT_VARIABLE.name}: '
f"{','.join(implemented)}")


def get_feature_flags():
"""
Expand Down Expand Up @@ -42,8 +55,15 @@ def is_feature_flag_set(flag):
:returns: True if the flag is set
:rtype: bool
"""
if flag in get_feature_flags():
if flag in IMPLEMENTED_FLAGS:
return True
elif flag in get_feature_flags():
if flag not in _REPORTED_USES:
if not _REPORTED_USES:
logger.warning(
'One or more feature flags have been enabled. '
'These features may be unstable and may change API or '
'behavior at any time.')
logger.warning(f'Enabling feature: {flag}')
_REPORTED_USES.add(flag)
return True
Expand Down
2 changes: 2 additions & 0 deletions test/test_entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore', message='.*entry_point.*deprecated.*', category=UserWarning)
warnings.filterwarnings(
'ignore', message='.*pkg_resources.*', category=DeprecationWarning)

from colcon_core.entry_point import EXTENSION_POINT_GROUP_NAME
from colcon_core.entry_point import get_all_entry_points
Expand Down
53 changes: 44 additions & 9 deletions test/test_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
from unittest.mock import patch

from colcon_core.feature_flags import check_implemented_flags
from colcon_core.feature_flags import FEATURE_FLAGS_ENVIRONMENT_VARIABLE
from colcon_core.feature_flags import get_feature_flags
from colcon_core.feature_flags import is_feature_flag_set
Expand Down Expand Up @@ -37,27 +38,41 @@ def feature_flags_value(request):
return request.param


@pytest.fixture
def feature_flag_reports(request):
reported_uses = patch('colcon_core.feature_flags._REPORTED_USES', set())
request.addfinalizer(reported_uses.stop)
reported_uses.start()
return reported_uses


@pytest.mark.parametrize(
'feature_flags_value',
_FLAGS_TO_TEST,
indirect=True)
@pytest.mark.usefixtures('feature_flags_value')
@pytest.mark.usefixtures('feature_flags_value', 'feature_flag_reports')
def test_flag_is_set():
assert is_feature_flag_set('foo')
with patch('colcon_core.feature_flags.logger.warning') as warn:
assert is_feature_flag_set('foo')
assert warn.call_count == 2
assert is_feature_flag_set('foo')
assert warn.call_count == 2


@pytest.mark.parametrize(
'feature_flags_value',
(None, *_FLAGS_TO_TEST),
indirect=True)
@pytest.mark.usefixtures('feature_flags_value')
@pytest.mark.usefixtures('feature_flags_value', 'feature_flag_reports')
def test_flag_not_set():
assert not is_feature_flag_set('')
assert not is_feature_flag_set('fo')
assert not is_feature_flag_set('oo')
assert not is_feature_flag_set('fooo')
assert not is_feature_flag_set('ffoo')
assert not is_feature_flag_set('qux')
with patch('colcon_core.feature_flags.logger.warning') as warn:
assert not is_feature_flag_set('')
assert not is_feature_flag_set('fo')
assert not is_feature_flag_set('oo')
assert not is_feature_flag_set('fooo')
assert not is_feature_flag_set('ffoo')
assert not is_feature_flag_set('qux')
assert warn.call_count == 0


@pytest.mark.parametrize(
Expand All @@ -69,3 +84,23 @@ def test_get_flags(feature_flags_value):
assert [
flag for flag in (feature_flags_value or ()) if flag
] == get_feature_flags()


@pytest.mark.parametrize('feature_flags_value', (('baz',),), indirect=True)
@pytest.mark.usefixtures('feature_flags_value')
def test_implemented():
with patch('colcon_core.feature_flags.IMPLEMENTED_FLAGS', {'foo'}):
with patch('colcon_core.feature_flags.logger.warning') as warn:
assert not is_feature_flag_set('bar')
assert warn.call_count == 0
assert is_feature_flag_set('baz')
assert warn.call_count == 2
assert is_feature_flag_set('foo')
assert warn.call_count == 2
check_implemented_flags()
assert warn.call_count == 2

with patch('colcon_core.feature_flags.IMPLEMENTED_FLAGS', {'baz'}):
with patch('colcon_core.feature_flags.logger.warning') as warn:
check_implemented_flags()
assert warn.call_count == 1

0 comments on commit 6566fa5

Please sign in to comment.