Source code for schrodinger.test.decorate_fflag
"""
Enable or disable a feature flag in a specific situation.  Can be used as a
decorator for a TestCase class or a test function, or as a context manager.
See nose_plugin_test.py for examples. Multiple feature flag toggling
decorators/contexts can be nested to define desired feature flag environment.
@copyright: (c) Schrodinger, LLC All rights reserved.
"""
import os
from contextlib import ContextDecorator
from unittest.mock import patch
from schrodinger.utils import mmutil
[docs]class enable_feature_flag(ContextDecorator):
    """
    Provides a decorator/context manager for enabling feature flags.  Methods
    are named to allow this to be used the same ways as Mock's patches:
    * As context manager
    * As function or test class decorator
    * Explicit start and stop.
    """
    on = 1
[docs]    def __init__(self, flag):
        self.flag = flag
        self.original_state = None 
[docs]    def start(self):
        """
        Starts the patching.  Used both in the decorator and the context
        manager.
        """
        current_environ = os.environ.get('SCHRODINGER_FEATURE_FLAGS', '')
        desired_environ = self._set_ff(self.flag, current_environ, self.on)
        # Some job control fflags must be set in this special variable to be
        # accessible from perl.
        perl_current_environ = os.environ.get(
            'SCHRODINGER_PERL_FEATURE_FLAGS_ENABLED', '')
        if self.flag not in perl_current_environ:
            perl_desired_environ = perl_current_environ + ' ' + self.flag
            perl_desired_environ.strip()
        else:
            perl_desired_environ = perl_current_environ
        self.environ_patcher = patch.dict(
            'os.environ', {
                'SCHRODINGER_FEATURE_FLAGS': desired_environ,
                'SCHRODINGER_PERL_FEATURE_FLAGS_ENABLED': perl_desired_environ
            })
        self.original_state = mmutil.feature_flag_is_enabled_s(self.flag)
        mmutil.feature_flags_set_user_state_s(self.flag, self.on)
        self.environ_patcher.start() 
[docs]    def stop(self):
        """
        Stops the patching.  Used both in the decorator context and the
        context manager context.
        """
        self.environ_patcher.stop()
        mmutil.feature_flags_set_user_state_s(self.flag, self.original_state) 
    def _onstr(self, on=False):
        """Return the +/- representation of a boolean value."""
        if on:
            return '+'
        else:
            return '-'
    def _set_ff(self, flagname, flagstr, on=True):
        """Create the featureflag environment variable string."""
        flags = set(flagstr.split())
        desired = self._onstr(on) + flagname
        opposite = self._onstr(not on) + flagname
        if opposite in flags:
            flags = flags - {opposite}
        flags.add(desired)
        return ' '.join(flags)
    def __enter__(self):
        """Enter the context manager."""
        self.start()
    def __exit__(self, exc_type, exc_value, traceback):
        """Exit the context manager."""
        self.stop() 
[docs]class disable_feature_flag(enable_feature_flag):
    """
    Disable a feature flag as either a context manager or a decorator for a
    unittest.TestCase or a test function.
    :type flag: str
    :param flag: Name of the feature flag to be disabled.
    """
    on = 0