Allow temporary modification of rules for :meth:`~.Expression.create`

    For every one of the given `classes`, protect the rules (processed by
    :func:`.match_replace` or :func:`.match_replace_binary`) associated with
    that class from modification beyond the managed context.
    Implies :func:`temporary_instance_cache`. If `clear` is given as
    True, all existing rules are temporarily cleared from the given classes on
    entering the managed context.

    Within the managed context, :meth:`~.Expression.add_rule` may be used for
    any class in `classes` to define local rules, or
    :meth:`~.Expression.del_rules` to disable specific existing rules (assuming
    `clear` is False). Upon leaving the managed context all original rules will
    be restored, removing any local rules.

    The `classes`' :obj:`simplifications <.Expression>` attribute is also
    protected from permanent modification. Locally modifying
    :obj:`simplifications <.Expression>` should be done with care, but allows
    complete control over the creation of expressions.

def temporary_rules(*classes, clear=False):
    """Allow temporary modification of rules for :meth:`~.Expression.create`

    For every one of the given `classes`, protect the rules (processed by
    :func:`.match_replace` or :func:`.match_replace_binary`) associated with
    that class from modification beyond the managed context.
    Implies :func:`temporary_instance_cache`. If `clear` is given as
    True, all existing rules are temporarily cleared from the given classes on
    entering the managed context.

    Within the managed context, :meth:`~.Expression.add_rule` may be used for
    any class in `classes` to define local rules, or
    :meth:`~.Expression.del_rules` to disable specific existing rules (assuming
    `clear` is False). Upon leaving the managed context all original rules will
    be restored, removing any local rules.

    The `classes`' :obj:`simplifications <.Expression>` attribute is also
    protected from permanent modification. Locally modifying
    :obj:`simplifications <.Expression>` should be done with care, but allows
    complete control over the creation of expressions.
    """
    orig_instances = []
    orig_rules = []
    orig_binary_rules = []
    orig_simplifications = []

    for cls in classes:
        orig_instances.append(cls._instances)
        cls._instances = {}
        orig_simplifications.append(cls.simplifications)
        cls.simplifications = cls.simplifications.copy()
        try:
            orig_rules.append(cls._rules)
            if clear:
                cls._rules = OrderedDict([])
            else:
                cls._rules = cls._rules.copy()
        except AttributeError:
            orig_rules.append(None)
        try:
            orig_binary_rules.append(cls._binary_rules)
            if clear:
                cls._binary_rules = OrderedDict([])
            else:
                cls._binary_rules = cls._binary_rules.copy()
        except AttributeError:
            orig_binary_rules.append(None)

    try:
        yield
    finally:
        for i, cls in enumerate(classes):
            cls._instances = orig_instances[i]
            cls.simplifications = orig_simplifications[i]
            if orig_rules[i] is not None:
                cls._rules = orig_rules[i]
            if orig_binary_rules[i] is not None:
                cls._binary_rules = orig_binary_rules[i]