{
    "file_diffs": [
        {
            "old_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Basic dialog-like interface for interactions in the terminal window\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom logging import getLogger\nlgr = getLogger('datalad.ui.dialog')\n\nlgr.log(5, \"Starting importing ui.dialog\")\n\nimport os\nimport sys\nimport time\n\nimport getpass\n\n#!!! OPT adds >100ms to import time!!!\n# from unittest.mock import patch\nfrom collections import deque\nfrom copy import copy\n\nfrom ..utils import auto_repr\nfrom ..utils import on_windows\nfrom .base import InteractiveUI\nfrom ..dochelpers import exc_str\n\n# Example APIs which might be useful to look for \"inspiration\"\n#  man debconf-devel\n#  man zenity\n#\n# \"Fancy\" output of progress etc in the terminal:\n# - docker has multiple simultaneous progressbars.  Apparently \"navigation\"\n#   is obtained with escape characters in the terminal.\n#   see docker/pkg/jsonmessage/jsonmessage.go or following snippet\n#\n#from time import sleep\n#import sys\n#\n#out = sys.stderr\n#for i in range(10):\n#  diff = 2\n#  if i:\n#      out.write(\"%c[%dA\" % (27, diff))\n#  out.write(\"%d\\n%d\\n\" % (i, i ** 2))\n#  sleep(0.5)\n#\n# They also use JSON representation for the message which might provide a nice abstraction\n# Other useful codes\n#         // <ESC>[2K = erase entire current line\n#        fmt.Fprintf(out, \"%c[2K\\r\", 27)\n# and code in docker: pkg/progressreader/progressreader.go pkg/streamformatter/streamformatter.go\n#\n# reference for ESC codes: http://ascii-table.com/ansi-escape-sequences.php\n\n\n@auto_repr\nclass ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)\n\n\n@auto_repr\nclass SilentConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a SilentProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import SilentProgressBar\n        return SilentProgressBar(*args, **kwargs)\n\n    def question(self, text, title=None, **kwargs):\n        msg = \"A non-interactive silent UI was asked for a response to a question: %s.\" % text\n        if title is not None:\n            msg += ' Title: %s.' % title\n        if not kwargs.get('hidden'):\n            kwargs_str = ', '.join(\n                ('%s=%r' % (k, v)\n                for k, v in kwargs.items()\n                if v is not None))\n            if kwargs_str:\n                msg += \" Additional arguments: %s\" % kwargs_str\n        else:\n            msg += \" Additional arguments are not shown because 'hidden' is set.\"\n        raise RuntimeError(msg)\n\n\n@auto_repr\nclass QuietConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a LogProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import LogProgressBar\n        return LogProgressBar(*args, **kwargs)\n\n\ndef getpass_echo(prompt='Password', stream=None):\n    \"\"\"Q&D workaround until we have proper 'centralized' UI -- just use getpass BUT enable echo\n    \"\"\"\n    if on_windows:\n        # Can't do anything fancy yet, so just ask the one without echo\n        return getpass.getpass(prompt=prompt, stream=stream)\n    else:\n        # We can mock patch termios so that ECHO is not turned OFF.\n        # Side-effect -- additional empty line is printed\n\n        # def _no_emptyline_write(out):\n        #     # Additional mock to prevent not needed empty line print since we do have echo\n        #     # doesn't work since we don't know the stream here really\n        #     if out == '\\n':\n        #         return\n        #     stream.write(out)\n        from unittest.mock import patch\n        with patch('termios.ECHO', 255 ** 2):\n            #patch.object(stream, 'write', _no_emptyline_write(stream)):\n            return getpass.getpass(prompt=prompt, stream=stream)\n\n\ndef _get_value(value, hidden):\n    return \"<hidden>\" if hidden else value\n\n\n@auto_repr\nclass DialogUI(ConsoleLog, InteractiveUI):\n\n    def __init__(self, *args, **kwargs):\n        super(DialogUI, self).__init__(*args, **kwargs)\n        # ATM doesn't make sense to print the same title for subsequent questions\n        # so we will store previous one and not show it if was the previous one shown\n        # within 5 seconds from prev question\n        self._prev_title = None\n        self._prev_title_time = 0\n\n    def input(self, prompt, hidden=False):\n        \"\"\"Request user input\n\n        Parameters\n        ----------\n        prompt: str\n          Prompt for the entry\n        \"\"\"\n        # if not hidden:\n        #     self.out.write(msg + \": \")\n        #     self.out.flush()  # not effective for stderr for some reason under annex\n        #\n        #     # TODO: raw_input works only if stdin was not controlled by\n        #     # (e.g. if coming from annex).  So we might need to do the\n        #     # same trick as get_pass() does while directly dealing with /dev/pty\n        #     # and provide per-OS handling with stdin being override\n        #     response = (raw_input if PY2 else input)()\n        # else:\n        return (getpass.getpass if hidden else getpass_echo)(prompt)\n\n    def question(self, text,\n                 title=None, choices=None,\n                 default=None,\n                 hidden=False,\n                 repeat=None):\n        # Do initial checks first\n        if default and choices and default not in choices:\n            raise ValueError(\"default value %r is not among choices: %s\"\n                             % (_get_value(default, hidden), choices))\n\n        msg = ''\n        if title and not (title == self._prev_title and time.time() - self._prev_title_time < 5):\n            # might not actually get displayed if all in/out redirected\n            # self.out.write(title + \"\\n\")\n            # so merge into msg for getpass\n            msg += title + os.linesep\n\n        def mark_default(x):\n            return \"[%s]\" % x \\\n                if default is not None and x == default \\\n                else x\n\n        if choices is not None:\n            msg += \"%s (choices: %s)\" % (text, ', '.join(map(mark_default, choices)))\n        elif default is not None:\n            msg += '{} [{}]'.format(text, default)\n        else:\n            msg += text\n        # Like this:\n        #Anaconda format:\n        #\n        #Question? [choice1|choice2]\n        #[default] >>> yes\n        attempt = 0\n        while True:\n            attempt += 1\n            if attempt >= 100:\n                raise RuntimeError(\"This is 100th attempt. Something really went wrong\")\n\n            response = self.input(\"{}: \".format(msg), hidden=hidden)\n            # TODO: dedicated option?  got annoyed by this one\n            # multiple times already, typically we are not defining\n            # new credentials where repetition would be needed.\n            if hidden and repeat is None:\n                repeat = hidden and choices is None\n\n            if repeat:\n                response_r = self.input('{} (repeat): '.format(msg), hidden=hidden)\n                if response != response_r:\n                    self.error(\"input mismatch, please start over\")\n                    continue\n\n            if response and '\\x03' in response:\n                # Ctrl-C is part of the response -> clearly we should not pretend it's all good\n                raise KeyboardInterrupt\n\n            if not response and default:\n                response = default\n                break\n\n            if choices and response not in choices:\n                self.error(\"%r is not among choices: %s. Repeat your answer\"\n                           % (_get_value(response, hidden), choices))\n                continue\n            break\n\n        self._prev_title = title\n        self._prev_title_time = time.time()\n\n        return response\n\n\nclass IPythonUI(DialogUI):\n    \"\"\"Custom to IPython frontend UI implementation\n\n    There is no way to discriminate between web notebook or qt console,\n    so we have just a single class for all.\n\n    TODO: investigate how to provide 'proper' displays for\n    IPython of progress bars so backend could choose the\n    appropriate one\n\n    \"\"\"\n\n    _tqdm_frontend = \"unknown\"\n\n    def input(self, prompt, hidden=False):\n        # We cannot and probably do not need to \"abuse\" termios\n        if not hidden:\n            self.out.write(prompt)\n            self.out.flush()\n            return input()\n        else:\n            return getpass.getpass(prompt=prompt)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the\n        interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        if self._tqdm_frontend == \"unknown\":\n            from .progressbars import tqdmProgressBar\n            try:\n                from tqdm import tqdm_notebook  # check if available etc\n                self.__class__._tqdm_frontend = 'ipython'\n            except Exception as exc:\n                lgr.warning(\n                    \"Regular progressbar will be used -- cannot import tqdm_notebook: %s\",\n                    exc_str(exc)\n                )\n                self.__class__._tqdm_frontend = None\n        if self._tqdm_frontend:\n            kwargs.update()\n        return super(IPythonUI, self).get_progressbar(\n                *args, frontend=self._tqdm_frontend, **kwargs)\n\n\n# poor man thingie for now\n@auto_repr\nclass UnderAnnexUI(DialogUI):\n    def __init__(self, specialremote=None, **kwargs):\n        if 'out' not in kwargs:\n            # to avoid buffering\n            # http://stackoverflow.com/a/181654/1265472\n            #kwargs['out'] = os.fdopen(sys.stderr.fileno(), 'w', 0)\n            # but wasn't effective! sp kist straogjt for now\n            kwargs['out'] = sys.stderr\n        super(UnderAnnexUI, self).__init__(**kwargs)\n        self.specialremote = specialremote\n\n    def set_specialremote(self, specialremote):\n        self.specialremote = specialremote\n\n    def get_progressbar(self, *args, **kwargs):\n        if self.specialremote:\n            kwargs = kwargs.copy()\n            kwargs['backend'] = 'annex-remote'\n            kwargs['remote'] = self.specialremote\n        return super(UnderAnnexUI, self).get_progressbar(\n                *args, **kwargs)\n\n\n@auto_repr\nclass UnderTestsUI(DialogUI):\n    \"\"\"UI to help with testing functionality requiring interaction\n\n    It will provide additional method to push responses to be provided,\n    and could be used as a context manager\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        super(UnderTestsUI, self).__init__(**kwargs)\n        self._responses = deque()\n\n    # TODO: possibly allow to provide expected messages etc, so we could\n    # test that those are the actual ones which were given\n    def add_responses(self, responses):\n        if not isinstance(responses, (list, tuple)):\n            responses = [responses]\n        self._responses += list(responses)\n        return self  # so we could use it as a context manager\n\n    def get_responses(self):\n        return self._responses\n\n    def clear_responses(self):\n        self._responses = deque()\n\n    def question(self, *args, **kwargs):\n        if not self._responses:\n            raise AssertionError(\n                \"We are asked for a response whenever none is left to give\"\n            )\n        return self._responses.popleft()\n\n    # Context manager mode of operation which would also verify that\n    # no responses left upon exiting\n    def __enter__(self):\n        pass\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        responses = copy(self._responses)\n        # we should clear the state so there is no side-effect\n        self.clear_responses()\n        assert not len(responses), \\\n            \"Still have some responses left: %s\" % repr(self._responses)\n\nlgr.log(5, \"Done importing ui.dialog\")\n",
            "new_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Basic dialog-like interface for interactions in the terminal window\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom logging import getLogger\nlgr = getLogger('datalad.ui.dialog')\n\nlgr.log(5, \"Starting importing ui.dialog\")\n\nimport os\nimport sys\nimport time\n\nimport getpass\n\n#!!! OPT adds >100ms to import time!!!\n# from unittest.mock import patch\nfrom collections import deque\nfrom copy import copy\n\nfrom ..utils import auto_repr\nfrom ..utils import on_windows\nfrom .base import InteractiveUI\nfrom ..dochelpers import exc_str\n\n# Example APIs which might be useful to look for \"inspiration\"\n#  man debconf-devel\n#  man zenity\n#\n# \"Fancy\" output of progress etc in the terminal:\n# - docker has multiple simultaneous progressbars.  Apparently \"navigation\"\n#   is obtained with escape characters in the terminal.\n#   see docker/pkg/jsonmessage/jsonmessage.go or following snippet\n#\n#from time import sleep\n#import sys\n#\n#out = sys.stderr\n#for i in range(10):\n#  diff = 2\n#  if i:\n#      out.write(\"%c[%dA\" % (27, diff))\n#  out.write(\"%d\\n%d\\n\" % (i, i ** 2))\n#  sleep(0.5)\n#\n# They also use JSON representation for the message which might provide a nice abstraction\n# Other useful codes\n#         // <ESC>[2K = erase entire current line\n#        fmt.Fprintf(out, \"%c[2K\\r\", 27)\n# and code in docker: pkg/progressreader/progressreader.go pkg/streamformatter/streamformatter.go\n#\n# reference for ESC codes: http://ascii-table.com/ansi-escape-sequences.php\n\n\n@auto_repr\nclass ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear',\n                     noninteractive_level=5)\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh',\n                     noninteractive_level=5)\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)\n\n\n@auto_repr\nclass SilentConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a SilentProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import SilentProgressBar\n        return SilentProgressBar(*args, **kwargs)\n\n    def question(self, text, title=None, **kwargs):\n        msg = \"A non-interactive silent UI was asked for a response to a question: %s.\" % text\n        if title is not None:\n            msg += ' Title: %s.' % title\n        if not kwargs.get('hidden'):\n            kwargs_str = ', '.join(\n                ('%s=%r' % (k, v)\n                for k, v in kwargs.items()\n                if v is not None))\n            if kwargs_str:\n                msg += \" Additional arguments: %s\" % kwargs_str\n        else:\n            msg += \" Additional arguments are not shown because 'hidden' is set.\"\n        raise RuntimeError(msg)\n\n\n@auto_repr\nclass QuietConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a LogProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import LogProgressBar\n        return LogProgressBar(*args, **kwargs)\n\n\ndef getpass_echo(prompt='Password', stream=None):\n    \"\"\"Q&D workaround until we have proper 'centralized' UI -- just use getpass BUT enable echo\n    \"\"\"\n    if on_windows:\n        # Can't do anything fancy yet, so just ask the one without echo\n        return getpass.getpass(prompt=prompt, stream=stream)\n    else:\n        # We can mock patch termios so that ECHO is not turned OFF.\n        # Side-effect -- additional empty line is printed\n\n        # def _no_emptyline_write(out):\n        #     # Additional mock to prevent not needed empty line print since we do have echo\n        #     # doesn't work since we don't know the stream here really\n        #     if out == '\\n':\n        #         return\n        #     stream.write(out)\n        from unittest.mock import patch\n        with patch('termios.ECHO', 255 ** 2):\n            #patch.object(stream, 'write', _no_emptyline_write(stream)):\n            return getpass.getpass(prompt=prompt, stream=stream)\n\n\ndef _get_value(value, hidden):\n    return \"<hidden>\" if hidden else value\n\n\n@auto_repr\nclass DialogUI(ConsoleLog, InteractiveUI):\n\n    def __init__(self, *args, **kwargs):\n        super(DialogUI, self).__init__(*args, **kwargs)\n        # ATM doesn't make sense to print the same title for subsequent questions\n        # so we will store previous one and not show it if was the previous one shown\n        # within 5 seconds from prev question\n        self._prev_title = None\n        self._prev_title_time = 0\n\n    def input(self, prompt, hidden=False):\n        \"\"\"Request user input\n\n        Parameters\n        ----------\n        prompt: str\n          Prompt for the entry\n        \"\"\"\n        # if not hidden:\n        #     self.out.write(msg + \": \")\n        #     self.out.flush()  # not effective for stderr for some reason under annex\n        #\n        #     # TODO: raw_input works only if stdin was not controlled by\n        #     # (e.g. if coming from annex).  So we might need to do the\n        #     # same trick as get_pass() does while directly dealing with /dev/pty\n        #     # and provide per-OS handling with stdin being override\n        #     response = (raw_input if PY2 else input)()\n        # else:\n        return (getpass.getpass if hidden else getpass_echo)(prompt)\n\n    def question(self, text,\n                 title=None, choices=None,\n                 default=None,\n                 hidden=False,\n                 repeat=None):\n        # Do initial checks first\n        if default and choices and default not in choices:\n            raise ValueError(\"default value %r is not among choices: %s\"\n                             % (_get_value(default, hidden), choices))\n\n        msg = ''\n        if title and not (title == self._prev_title and time.time() - self._prev_title_time < 5):\n            # might not actually get displayed if all in/out redirected\n            # self.out.write(title + \"\\n\")\n            # so merge into msg for getpass\n            msg += title + os.linesep\n\n        def mark_default(x):\n            return \"[%s]\" % x \\\n                if default is not None and x == default \\\n                else x\n\n        if choices is not None:\n            msg += \"%s (choices: %s)\" % (text, ', '.join(map(mark_default, choices)))\n        elif default is not None:\n            msg += '{} [{}]'.format(text, default)\n        else:\n            msg += text\n        # Like this:\n        #Anaconda format:\n        #\n        #Question? [choice1|choice2]\n        #[default] >>> yes\n        attempt = 0\n        while True:\n            attempt += 1\n            if attempt >= 100:\n                raise RuntimeError(\"This is 100th attempt. Something really went wrong\")\n\n            response = self.input(\"{}: \".format(msg), hidden=hidden)\n            # TODO: dedicated option?  got annoyed by this one\n            # multiple times already, typically we are not defining\n            # new credentials where repetition would be needed.\n            if hidden and repeat is None:\n                repeat = hidden and choices is None\n\n            if repeat:\n                response_r = self.input('{} (repeat): '.format(msg), hidden=hidden)\n                if response != response_r:\n                    self.error(\"input mismatch, please start over\")\n                    continue\n\n            if response and '\\x03' in response:\n                # Ctrl-C is part of the response -> clearly we should not pretend it's all good\n                raise KeyboardInterrupt\n\n            if not response and default:\n                response = default\n                break\n\n            if choices and response not in choices:\n                self.error(\"%r is not among choices: %s. Repeat your answer\"\n                           % (_get_value(response, hidden), choices))\n                continue\n            break\n\n        self._prev_title = title\n        self._prev_title_time = time.time()\n\n        return response\n\n\nclass IPythonUI(DialogUI):\n    \"\"\"Custom to IPython frontend UI implementation\n\n    There is no way to discriminate between web notebook or qt console,\n    so we have just a single class for all.\n\n    TODO: investigate how to provide 'proper' displays for\n    IPython of progress bars so backend could choose the\n    appropriate one\n\n    \"\"\"\n\n    _tqdm_frontend = \"unknown\"\n\n    def input(self, prompt, hidden=False):\n        # We cannot and probably do not need to \"abuse\" termios\n        if not hidden:\n            self.out.write(prompt)\n            self.out.flush()\n            return input()\n        else:\n            return getpass.getpass(prompt=prompt)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the\n        interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        if self._tqdm_frontend == \"unknown\":\n            from .progressbars import tqdmProgressBar\n            try:\n                from tqdm import tqdm_notebook  # check if available etc\n                self.__class__._tqdm_frontend = 'ipython'\n            except Exception as exc:\n                lgr.warning(\n                    \"Regular progressbar will be used -- cannot import tqdm_notebook: %s\",\n                    exc_str(exc)\n                )\n                self.__class__._tqdm_frontend = None\n        if self._tqdm_frontend:\n            kwargs.update()\n        return super(IPythonUI, self).get_progressbar(\n                *args, frontend=self._tqdm_frontend, **kwargs)\n\n\n# poor man thingie for now\n@auto_repr\nclass UnderAnnexUI(DialogUI):\n    def __init__(self, specialremote=None, **kwargs):\n        if 'out' not in kwargs:\n            # to avoid buffering\n            # http://stackoverflow.com/a/181654/1265472\n            #kwargs['out'] = os.fdopen(sys.stderr.fileno(), 'w', 0)\n            # but wasn't effective! sp kist straogjt for now\n            kwargs['out'] = sys.stderr\n        super(UnderAnnexUI, self).__init__(**kwargs)\n        self.specialremote = specialremote\n\n    def set_specialremote(self, specialremote):\n        self.specialremote = specialremote\n\n    def get_progressbar(self, *args, **kwargs):\n        if self.specialremote:\n            kwargs = kwargs.copy()\n            kwargs['backend'] = 'annex-remote'\n            kwargs['remote'] = self.specialremote\n        return super(UnderAnnexUI, self).get_progressbar(\n                *args, **kwargs)\n\n\n@auto_repr\nclass UnderTestsUI(DialogUI):\n    \"\"\"UI to help with testing functionality requiring interaction\n\n    It will provide additional method to push responses to be provided,\n    and could be used as a context manager\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        super(UnderTestsUI, self).__init__(**kwargs)\n        self._responses = deque()\n\n    # TODO: possibly allow to provide expected messages etc, so we could\n    # test that those are the actual ones which were given\n    def add_responses(self, responses):\n        if not isinstance(responses, (list, tuple)):\n            responses = [responses]\n        self._responses += list(responses)\n        return self  # so we could use it as a context manager\n\n    def get_responses(self):\n        return self._responses\n\n    def clear_responses(self):\n        self._responses = deque()\n\n    def question(self, *args, **kwargs):\n        if not self._responses:\n            raise AssertionError(\n                \"We are asked for a response whenever none is left to give\"\n            )\n        return self._responses.popleft()\n\n    # Context manager mode of operation which would also verify that\n    # no responses left upon exiting\n    def __enter__(self):\n        pass\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        responses = copy(self._responses)\n        # we should clear the state so there is no side-effect\n        self.clear_responses()\n        assert not len(responses), \\\n            \"Still have some responses left: %s\" % repr(self._responses)\n\nlgr.log(5, \"Done importing ui.dialog\")\n",
            "header": {
                "file": {
                    "path": "datalad/ui/dialog.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "cf86ecdba",
                "new_commit_hash": "7bed97683",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/ui/dialog.py"
            },
            "plus_file": {
                "path": "b/datalad/ui/dialog.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 72,
                            "length": 11
                        },
                        "new_range": {
                            "start": 72,
                            "length": 13
                        },
                        "section": "class ConsoleLog(object):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def message(self, msg, cr='\\n'):",
                                "type": "context"
                            },
                            {
                                "content": "        from datalad.log import log_progress",
                                "type": "context"
                            },
                            {
                                "content": "        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')",
                                "type": "deleted"
                            },
                            {
                                "content": "        log_progress(lgr.info, None, 'Clear progress bars', maint='clear',",
                                "type": "added"
                            },
                            {
                                "content": "                     noninteractive_level=5)",
                                "type": "added"
                            },
                            {
                                "content": "        self.out.write(msg)",
                                "type": "context"
                            },
                            {
                                "content": "        if cr:",
                                "type": "context"
                            },
                            {
                                "content": "            self.out.write(cr)",
                                "type": "context"
                            },
                            {
                                "content": "        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')",
                                "type": "deleted"
                            },
                            {
                                "content": "        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh',",
                                "type": "added"
                            },
                            {
                                "content": "                     noninteractive_level=5)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def error(self, error):",
                                "type": "context"
                            },
                            {
                                "content": "        self.out.write(\"ERROR: %s\\n\" % error)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/ui/dialog.py",
                            "type": "method",
                            "name": "ConsoleLog.message",
                            "content": "class ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 73,
                            "end_lineno": 79,
                            "parent": {
                                "file_name": "datalad/ui/dialog.py",
                                "type": "class",
                                "name": "ConsoleLog",
                                "content": "class ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 66,
                                "end_lineno": 113,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "datalad/ui/dialog.py",
                            "type": "class",
                            "name": "ConsoleLog",
                            "content": "class ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 66,
                            "end_lineno": 113,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"tests for dialog UI \"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom io import StringIO\nimport builtins\n\nfrom unittest.mock import (\n    call,\n    patch,\n)\nfrom ...tests.utils import (\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    ok_endswith,\n    ok_startswith,\n)\nfrom ..dialog import (\n    DialogUI,\n    IPythonUI,\n)\nfrom datalad.ui.progressbars import progressbars\n\n\ndef patch_input(**kwargs):\n    \"\"\"A helper to provide mocked cm patching input function which was renamed in PY3\"\"\"\n    return patch.object(builtins, 'input', **kwargs)\n\n\ndef patch_getpass(**kwargs):\n    return patch('getpass.getpass', **kwargs)\n\n\ndef test_yesno():\n    for expected_value, defaults in {True: ('yes', True),\n                                     False: ('no', False)}.items():\n        for d in defaults:\n            with patch_getpass(return_value=''):\n                out = StringIO()\n                response = DialogUI(out=out).yesno(\"?\", default=d)\n                eq_(response, expected_value)\n\n\ndef test_question_choices():\n\n    # TODO: come up with a reusable fixture for testing here\n\n    choices = {\n        'a': '[a], b, cc',\n        'b': 'a, [b], cc',\n        'cc': 'a, b, [cc]'\n    }\n\n    for hidden in (True, False):\n        for default_value in ['a', 'b']:\n            choices_str = choices[default_value]\n            for entered_value, expected_value in [(default_value, default_value),\n                                                  ('', default_value),\n                                                  ('cc', 'cc')]:\n                with patch_getpass(return_value=entered_value) as gpcm:\n                    out = StringIO()\n                    response = DialogUI(out=out).question(\n                        \"prompt\", choices=sorted(choices), default=default_value,\n                        hidden=hidden\n                    )\n                    # .assert_called_once() is not available on older mock's\n                    # e.g. on  1.3.0 on nd16.04\n                    eq_(gpcm.call_count, 1)  # should have asked only once\n                    eq_(response, expected_value)\n                    # getpass doesn't use out -- goes straight to the terminal\n                    eq_(out.getvalue(), '')\n                    # TODO: may be test that the prompt was passed as a part of the getpass arg\n                    #eq_(out.getvalue(), 'prompt (choices: %s): ' % choices_str)\n\n    # check some expected exceptions to be thrown\n    out = StringIO()\n    ui = DialogUI(out=out)\n    assert_raises(ValueError, ui.question, \"prompt\", choices=['a'], default='b')\n    eq_(out.getvalue(), '')\n\n    with patch_getpass(return_value='incorrect'):\n        assert_raises(RuntimeError, ui.question, \"prompt\", choices=['a', 'b'])\n    assert_re_in(\".*ERROR: .incorrect. is not among choices.*\", out.getvalue())\n\n\ndef test_hidden_doubleentry():\n    # In above test due to 'choices' there were no double entry for a hidden\n    out = StringIO()\n    ui = DialogUI(out=out)\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: '), call('? (repeat): ')])\n\n    # explicitly request no repeats\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True, repeat=False)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: ')])\n\n\ndef _test_progress_bar(backend, len, increment):\n    out = StringIO()\n    fill_str = ('123456890' * (len//10))[:len]\n    pb = DialogUI(out).get_progressbar(\n        'label', fill_str, total=10, backend=backend)\n    pb.start()\n    # we can't increment 11 times\n    SILENT_BACKENDS = ('annex-remote', 'silent', 'none')\n    ONLY_THE_END_BACKENDS = ('log',)\n    for x in range(11):\n        if not (increment and x == 0):\n            # do not increment on 0\n            pb.update(x if not increment else 1, increment=increment)\n        #out.flush()  # needed atm... no longer?\n        # Progress bar is having 0.1 sec between updates by default, so\n        # we could either sleep:\n        #import time; time.sleep(0.1)\n        # or just force the refresh\n        pb.refresh()\n        pstr = out.getvalue()\n        if backend not in SILENT_BACKENDS + ONLY_THE_END_BACKENDS:  # no str repr\n            ok_startswith(pstr.lstrip('\\r'), 'label:')\n            assert_re_in(r'.*\\b%d%%.*' % (10*x), pstr)\n        if backend == 'progressbar':\n            assert_in('ETA', pstr)\n    pb.finish()\n    output = out.getvalue()\n    if backend not in SILENT_BACKENDS:\n        # returns back and there is no spurious newline\n        if output:\n            ok_endswith(output, '\\r')\n\n\ndef test_progress_bar():\n    # More of smoke testing given various lengths of fill_text\n    for backend in progressbars:\n        for l in 0, 4, 10, 1000:\n            for increment in True, False:\n                yield _test_progress_bar, backend, l, increment\n\n\ndef test_IPythonUI():\n    # largely just smoke tests to see if nothing is horribly bad\n    with patch_input(return_value='a'):\n        out = StringIO()\n        response = IPythonUI(out=out).question(\n            \"prompt\", choices=sorted(['b', 'a'])\n        )\n        eq_(response, 'a')\n        eq_(out.getvalue(), 'prompt (choices: a, b): ')\n\n    ui = IPythonUI()\n    pbar = ui.get_progressbar(total=10)\n    assert_in('notebook', str(pbar._tqdm))\n\n\ndef test_silent_question():\n    # SilentConsoleLog must not be asked questions.\n    # If it is asked, RuntimeError would be thrown with details to help\n    # troubleshooting WTF is happening\n    from ..dialog import SilentConsoleLog\n    ui = SilentConsoleLog()\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\")\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\", choices=['secret1'], hidden=True)\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n    assert_not_in('secret1', str(cme.exception))\n    assert_in('not shown', str(cme.exception))\n\n    # additional kwargs, no title, choices\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"q\", choices=['secret1'])\n    assert_in('secret1', str(cme.exception))",
            "new_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"tests for dialog UI \"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom io import StringIO\nimport builtins\n\nfrom unittest.mock import (\n    call,\n    patch,\n)\n\nfrom datalad.utils import swallow_logs\nfrom ...tests.utils import (\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    ok_endswith,\n    ok_startswith,\n)\nfrom ..dialog import (\n    ConsoleLog,\n    DialogUI,\n    IPythonUI,\n)\nfrom datalad.ui.progressbars import progressbars\n\n\ndef patch_input(**kwargs):\n    \"\"\"A helper to provide mocked cm patching input function which was renamed in PY3\"\"\"\n    return patch.object(builtins, 'input', **kwargs)\n\n\ndef patch_getpass(**kwargs):\n    return patch('getpass.getpass', **kwargs)\n\n\ndef test_yesno():\n    for expected_value, defaults in {True: ('yes', True),\n                                     False: ('no', False)}.items():\n        for d in defaults:\n            with patch_getpass(return_value=''):\n                out = StringIO()\n                response = DialogUI(out=out).yesno(\"?\", default=d)\n                eq_(response, expected_value)\n\n\ndef test_question_choices():\n\n    # TODO: come up with a reusable fixture for testing here\n\n    choices = {\n        'a': '[a], b, cc',\n        'b': 'a, [b], cc',\n        'cc': 'a, b, [cc]'\n    }\n\n    for hidden in (True, False):\n        for default_value in ['a', 'b']:\n            choices_str = choices[default_value]\n            for entered_value, expected_value in [(default_value, default_value),\n                                                  ('', default_value),\n                                                  ('cc', 'cc')]:\n                with patch_getpass(return_value=entered_value) as gpcm:\n                    out = StringIO()\n                    response = DialogUI(out=out).question(\n                        \"prompt\", choices=sorted(choices), default=default_value,\n                        hidden=hidden\n                    )\n                    # .assert_called_once() is not available on older mock's\n                    # e.g. on  1.3.0 on nd16.04\n                    eq_(gpcm.call_count, 1)  # should have asked only once\n                    eq_(response, expected_value)\n                    # getpass doesn't use out -- goes straight to the terminal\n                    eq_(out.getvalue(), '')\n                    # TODO: may be test that the prompt was passed as a part of the getpass arg\n                    #eq_(out.getvalue(), 'prompt (choices: %s): ' % choices_str)\n\n    # check some expected exceptions to be thrown\n    out = StringIO()\n    ui = DialogUI(out=out)\n    assert_raises(ValueError, ui.question, \"prompt\", choices=['a'], default='b')\n    eq_(out.getvalue(), '')\n\n    with patch_getpass(return_value='incorrect'):\n        assert_raises(RuntimeError, ui.question, \"prompt\", choices=['a', 'b'])\n    assert_re_in(\".*ERROR: .incorrect. is not among choices.*\", out.getvalue())\n\n\ndef test_hidden_doubleentry():\n    # In above test due to 'choices' there were no double entry for a hidden\n    out = StringIO()\n    ui = DialogUI(out=out)\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: '), call('? (repeat): ')])\n\n    # explicitly request no repeats\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True, repeat=False)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: ')])\n\n\ndef _test_progress_bar(backend, len, increment):\n    out = StringIO()\n    fill_str = ('123456890' * (len//10))[:len]\n    pb = DialogUI(out).get_progressbar(\n        'label', fill_str, total=10, backend=backend)\n    pb.start()\n    # we can't increment 11 times\n    SILENT_BACKENDS = ('annex-remote', 'silent', 'none')\n    ONLY_THE_END_BACKENDS = ('log',)\n    for x in range(11):\n        if not (increment and x == 0):\n            # do not increment on 0\n            pb.update(x if not increment else 1, increment=increment)\n        #out.flush()  # needed atm... no longer?\n        # Progress bar is having 0.1 sec between updates by default, so\n        # we could either sleep:\n        #import time; time.sleep(0.1)\n        # or just force the refresh\n        pb.refresh()\n        pstr = out.getvalue()\n        if backend not in SILENT_BACKENDS + ONLY_THE_END_BACKENDS:  # no str repr\n            ok_startswith(pstr.lstrip('\\r'), 'label:')\n            assert_re_in(r'.*\\b%d%%.*' % (10*x), pstr)\n        if backend == 'progressbar':\n            assert_in('ETA', pstr)\n    pb.finish()\n    output = out.getvalue()\n    if backend not in SILENT_BACKENDS:\n        # returns back and there is no spurious newline\n        if output:\n            ok_endswith(output, '\\r')\n\n\ndef test_progress_bar():\n    # More of smoke testing given various lengths of fill_text\n    for backend in progressbars:\n        for l in 0, 4, 10, 1000:\n            for increment in True, False:\n                yield _test_progress_bar, backend, l, increment\n\n\ndef test_IPythonUI():\n    # largely just smoke tests to see if nothing is horribly bad\n    with patch_input(return_value='a'):\n        out = StringIO()\n        response = IPythonUI(out=out).question(\n            \"prompt\", choices=sorted(['b', 'a'])\n        )\n        eq_(response, 'a')\n        eq_(out.getvalue(), 'prompt (choices: a, b): ')\n\n    ui = IPythonUI()\n    pbar = ui.get_progressbar(total=10)\n    assert_in('notebook', str(pbar._tqdm))\n\n\ndef test_silent_question():\n    # SilentConsoleLog must not be asked questions.\n    # If it is asked, RuntimeError would be thrown with details to help\n    # troubleshooting WTF is happening\n    from ..dialog import SilentConsoleLog\n    ui = SilentConsoleLog()\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\")\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\", choices=['secret1'], hidden=True)\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n    assert_not_in('secret1', str(cme.exception))\n    assert_in('not shown', str(cme.exception))\n\n    # additional kwargs, no title, choices\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"q\", choices=['secret1'])\n    assert_in('secret1', str(cme.exception))\n\n\n@patch(\"datalad.log.is_interactive\", lambda: False)\ndef test_message_pbar_state_logging_is_demoted():\n    from datalad.log import LoggerHelper\n\n    name = \"dl-test\"\n    lgr = LoggerHelper(name).get_initialized_logger()\n    ui = ConsoleLog()\n\n    with patch(\"datalad.ui.dialog.lgr\", lgr):\n        with swallow_logs(name=name, new_level=20) as cml:\n            ui.message(\"testing 0\")\n            assert_not_in(\"Clear progress bars\", cml.out)\n            assert_not_in(\"Refresh progress bars\", cml.out)\n        with swallow_logs(name=name, new_level=5) as cml:\n            ui.message(\"testing 1\")\n            assert_in(\"Clear progress bars\", cml.out)\n            assert_in(\"Refresh progress bars\", cml.out)\n",
            "header": {
                "file": {
                    "path": "datalad/ui/tests/test_dialog.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "c7b5e9ea1",
                "new_commit_hash": "f104af9c2",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/ui/tests/test_dialog.py"
            },
            "plus_file": {
                "path": "b/datalad/ui/tests/test_dialog.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 17,
                            "length": 6
                        },
                        "new_range": {
                            "start": 17,
                            "length": 8
                        },
                        "section": "from unittest.mock import ("
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    call,",
                                "type": "context"
                            },
                            {
                                "content": "    patch,",
                                "type": "context"
                            },
                            {
                                "content": ")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "from datalad.utils import swallow_logs",
                                "type": "added"
                            },
                            {
                                "content": "from ...tests.utils import (",
                                "type": "context"
                            },
                            {
                                "content": "    assert_in,",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in,",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/ui/tests/test_dialog.py",
                            "type": "statement",
                            "name": "",
                            "content": "from datalad.utils import swallow_logs",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 21,
                            "end_lineno": 21,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 27,
                            "length": 6
                        },
                        "new_range": {
                            "start": 29,
                            "length": 7
                        },
                        "section": "from ...tests.utils import ("
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    ok_startswith,",
                                "type": "context"
                            },
                            {
                                "content": ")",
                                "type": "context"
                            },
                            {
                                "content": "from ..dialog import (",
                                "type": "context"
                            },
                            {
                                "content": "    ConsoleLog,",
                                "type": "added"
                            },
                            {
                                "content": "    DialogUI,",
                                "type": "context"
                            },
                            {
                                "content": "    IPythonUI,",
                                "type": "context"
                            },
                            {
                                "content": ")",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/ui/tests/test_dialog.py",
                            "type": "statement",
                            "name": "",
                            "content": "from ..dialog import (\n    ConsoleLog,\n    DialogUI,\n    IPythonUI,\n)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 31,
                            "end_lineno": 35,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 187,
                            "length": 4
                        },
                        "new_range": {
                            "start": 190,
                            "length": 23
                        },
                        "section": "def test_silent_question():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    # additional kwargs, no title, choices",
                                "type": "context"
                            },
                            {
                                "content": "    with assert_raises(RuntimeError) as cme:",
                                "type": "context"
                            },
                            {
                                "content": "        ui.question(\"q\", choices=['secret1'])",
                                "type": "context"
                            },
                            {
                                "content": "    assert_in('secret1', str(cme.exception))",
                                "type": "deleted"
                            },
                            {
                                "content": "No newline at end of file",
                                "type": "note"
                            },
                            {
                                "content": "    assert_in('secret1', str(cme.exception))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "@patch(\"datalad.log.is_interactive\", lambda: False)",
                                "type": "added"
                            },
                            {
                                "content": "def test_message_pbar_state_logging_is_demoted():",
                                "type": "added"
                            },
                            {
                                "content": "    from datalad.log import LoggerHelper",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    name = \"dl-test\"",
                                "type": "added"
                            },
                            {
                                "content": "    lgr = LoggerHelper(name).get_initialized_logger()",
                                "type": "added"
                            },
                            {
                                "content": "    ui = ConsoleLog()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    with patch(\"datalad.ui.dialog.lgr\", lgr):",
                                "type": "added"
                            },
                            {
                                "content": "        with swallow_logs(name=name, new_level=20) as cml:",
                                "type": "added"
                            },
                            {
                                "content": "            ui.message(\"testing 0\")",
                                "type": "added"
                            },
                            {
                                "content": "            assert_not_in(\"Clear progress bars\", cml.out)",
                                "type": "added"
                            },
                            {
                                "content": "            assert_not_in(\"Refresh progress bars\", cml.out)",
                                "type": "added"
                            },
                            {
                                "content": "        with swallow_logs(name=name, new_level=5) as cml:",
                                "type": "added"
                            },
                            {
                                "content": "            ui.message(\"testing 1\")",
                                "type": "added"
                            },
                            {
                                "content": "            assert_in(\"Clear progress bars\", cml.out)",
                                "type": "added"
                            },
                            {
                                "content": "            assert_in(\"Refresh progress bars\", cml.out)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/ui/tests/test_dialog.py",
                            "type": "function",
                            "name": "test_silent_question",
                            "content": "def test_silent_question():\n    # SilentConsoleLog must not be asked questions.\n    # If it is asked, RuntimeError would be thrown with details to help\n    # troubleshooting WTF is happening\n    from ..dialog import SilentConsoleLog\n    ui = SilentConsoleLog()\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\")\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\", choices=['secret1'], hidden=True)\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n    assert_not_in('secret1', str(cme.exception))\n    assert_in('not shown', str(cme.exception))\n\n    # additional kwargs, no title, choices\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"q\", choices=['secret1'])\n    assert_in('secret1', str(cme.exception))",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 171,
                            "end_lineno": 190,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "datalad/ui/tests/test_dialog.py",
                            "type": "function",
                            "name": "test_message_pbar_state_logging_is_demoted",
                            "content": "def test_message_pbar_state_logging_is_demoted():\n    from datalad.log import LoggerHelper\n\n    name = \"dl-test\"\n    lgr = LoggerHelper(name).get_initialized_logger()\n    ui = ConsoleLog()\n\n    with patch(\"datalad.ui.dialog.lgr\", lgr):\n        with swallow_logs(name=name, new_level=20) as cml:\n            ui.message(\"testing 0\")\n            assert_not_in(\"Clear progress bars\", cml.out)\n            assert_not_in(\"Refresh progress bars\", cml.out)\n        with swallow_logs(name=name, new_level=5) as cml:\n            ui.message(\"testing 1\")\n            assert_in(\"Clear progress bars\", cml.out)\n            assert_in(\"Refresh progress bars\", cml.out)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 197,
                            "end_lineno": 212,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "786968ef689c4a4c421da32d6eb71552fda7a4f6^",
    "new_commit_hash": "786968ef689c4a4c421da32d6eb71552fda7a4f6",
    "commit_message": "ENH: ui.message: Hide non-interactive log_progress output by default\n\nb7e57dfa2 (BF: Prevent ugly interaction or result and progress\nreporting, 2020-05-20) taught message() to clear progress bars before\nshowing its message, refreshing them after.  For non-interactive\noperations (e.g., piping datalad output), that dumps a lot of these\nirrelevant messages to the caller.\n\nHide these away by using noninteractive_level to filter them out\nunless the logging level is a very noisy 5.\n\nCloses #4570.",
    "commit_date": "2020-05-25T13:07:43-04:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "786968ef689c4a4c421da32d6eb71552fda7a4f6",
    "test_file_codes": [
        "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"tests for dialog UI \"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom io import StringIO\nimport builtins\n\nfrom unittest.mock import (\n    call,\n    patch,\n)\n\nfrom datalad.utils import swallow_logs\nfrom datalad.tests.utils import (\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    ok_endswith,\n    ok_startswith,\n)\nfrom datalad.ui.dialog import (\n    ConsoleLog,\n    DialogUI,\n    IPythonUI,\n)\nfrom datalad.ui.progressbars import progressbars\n\n\ndef patch_input(**kwargs):\n    \"\"\"A helper to provide mocked cm patching input function which was renamed in PY3\"\"\"\n    return patch.object(builtins, 'input', **kwargs)\n\n\ndef patch_getpass(**kwargs):\n    return patch('getpass.getpass', **kwargs)\n\n\ndef test_yesno():\n    for expected_value, defaults in {True: ('yes', True),\n                                     False: ('no', False)}.items():\n        for d in defaults:\n            with patch_getpass(return_value=''):\n                out = StringIO()\n                response = DialogUI(out=out).yesno(\"?\", default=d)\n                eq_(response, expected_value)\n\n\ndef test_question_choices():\n\n    # TODO: come up with a reusable fixture for testing here\n\n    choices = {\n        'a': '[a], b, cc',\n        'b': 'a, [b], cc',\n        'cc': 'a, b, [cc]'\n    }\n\n    for hidden in (True, False):\n        for default_value in ['a', 'b']:\n            choices_str = choices[default_value]\n            for entered_value, expected_value in [(default_value, default_value),\n                                                  ('', default_value),\n                                                  ('cc', 'cc')]:\n                with patch_getpass(return_value=entered_value) as gpcm:\n                    out = StringIO()\n                    response = DialogUI(out=out).question(\n                        \"prompt\", choices=sorted(choices), default=default_value,\n                        hidden=hidden\n                    )\n                    # .assert_called_once() is not available on older mock's\n                    # e.g. on  1.3.0 on nd16.04\n                    eq_(gpcm.call_count, 1)  # should have asked only once\n                    eq_(response, expected_value)\n                    # getpass doesn't use out -- goes straight to the terminal\n                    eq_(out.getvalue(), '')\n                    # TODO: may be test that the prompt was passed as a part of the getpass arg\n                    #eq_(out.getvalue(), 'prompt (choices: %s): ' % choices_str)\n\n    # check some expected exceptions to be thrown\n    out = StringIO()\n    ui = DialogUI(out=out)\n    assert_raises(ValueError, ui.question, \"prompt\", choices=['a'], default='b')\n    eq_(out.getvalue(), '')\n\n    with patch_getpass(return_value='incorrect'):\n        assert_raises(RuntimeError, ui.question, \"prompt\", choices=['a', 'b'])\n    assert_re_in(\".*ERROR: .incorrect. is not among choices.*\", out.getvalue())\n\n\ndef test_hidden_doubleentry():\n    # In above test due to 'choices' there were no double entry for a hidden\n    out = StringIO()\n    ui = DialogUI(out=out)\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: '), call('? (repeat): ')])\n\n    # explicitly request no repeats\n    with patch_getpass(return_value='ab') as gpcm:\n        response = ui.question(\n            \"?\", hidden=True, repeat=False)\n        eq_(response, 'ab')\n        gpcm.assert_has_calls([call('?: ')])\n\n\ndef _test_progress_bar(backend, len, increment):\n    out = StringIO()\n    fill_str = ('123456890' * (len//10))[:len]\n    pb = DialogUI(out).get_progressbar(\n        'label', fill_str, total=10, backend=backend)\n    pb.start()\n    # we can't increment 11 times\n    SILENT_BACKENDS = ('annex-remote', 'silent', 'none')\n    ONLY_THE_END_BACKENDS = ('log',)\n    for x in range(11):\n        if not (increment and x == 0):\n            # do not increment on 0\n            pb.update(x if not increment else 1, increment=increment)\n        #out.flush()  # needed atm... no longer?\n        # Progress bar is having 0.1 sec between updates by default, so\n        # we could either sleep:\n        #import time; time.sleep(0.1)\n        # or just force the refresh\n        pb.refresh()\n        pstr = out.getvalue()\n        if backend not in SILENT_BACKENDS + ONLY_THE_END_BACKENDS:  # no str repr\n            ok_startswith(pstr.lstrip('\\r'), 'label:')\n            assert_re_in(r'.*\\b%d%%.*' % (10*x), pstr)\n        if backend == 'progressbar':\n            assert_in('ETA', pstr)\n    pb.finish()\n    output = out.getvalue()\n    if backend not in SILENT_BACKENDS:\n        # returns back and there is no spurious newline\n        if output:\n            ok_endswith(output, '\\r')\n\n\ndef test_progress_bar():\n    # More of smoke testing given various lengths of fill_text\n    for backend in progressbars:\n        for l in 0, 4, 10, 1000:\n            for increment in True, False:\n                yield _test_progress_bar, backend, l, increment\n\n\ndef test_IPythonUI():\n    # largely just smoke tests to see if nothing is horribly bad\n    with patch_input(return_value='a'):\n        out = StringIO()\n        response = IPythonUI(out=out).question(\n            \"prompt\", choices=sorted(['b', 'a'])\n        )\n        eq_(response, 'a')\n        eq_(out.getvalue(), 'prompt (choices: a, b): ')\n\n    ui = IPythonUI()\n    pbar = ui.get_progressbar(total=10)\n    assert_in('notebook', str(pbar._tqdm))\n\n\ndef test_silent_question():\n    # SilentConsoleLog must not be asked questions.\n    # If it is asked, RuntimeError would be thrown with details to help\n    # troubleshooting WTF is happening\n    from datalad.ui.dialog import SilentConsoleLog\n    ui = SilentConsoleLog()\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\")\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"could you help me\", title=\"Pretty please\", choices=['secret1'], hidden=True)\n    assert_in('question: could you help me. Title: Pretty please.', str(cme.exception))\n    assert_not_in('secret1', str(cme.exception))\n    assert_in('not shown', str(cme.exception))\n\n    # additional kwargs, no title, choices\n    with assert_raises(RuntimeError) as cme:\n        ui.question(\"q\", choices=['secret1'])\n    assert_in('secret1', str(cme.exception))\n\n\n@patch(\"datalad.log.is_interactive\", lambda: False)\ndef test_message_pbar_state_logging_is_demoted():\n    from datalad.log import LoggerHelper\n\n    name = \"dl-test\"\n    lgr = LoggerHelper(name).get_initialized_logger()\n    ui = ConsoleLog()\n\n    with patch(\"datalad.ui.dialog.lgr\", lgr):\n        with swallow_logs(name=name, new_level=20) as cml:\n            ui.message(\"testing 0\")\n            assert_not_in(\"Clear progress bars\", cml.out)\n            assert_not_in(\"Refresh progress bars\", cml.out)\n        with swallow_logs(name=name, new_level=5) as cml:\n            ui.message(\"testing 1\")\n            assert_in(\"Clear progress bars\", cml.out)\n            assert_in(\"Refresh progress bars\", cml.out)\n",
        "import pytest\nimport tempfile\nimport os\n\n\n@pytest.fixture\ndef path():\n    with tempfile.TemporaryDirectory() as tmpdir:\n        yield tmpdir\n\n\n@pytest.fixture(autouse=True)\ndef setup_git_config():\n    with tempfile.TemporaryDirectory() as home:\n        old_home = os.environ.get(\"HOME\")\n        os.environ[\"HOME\"] = home\n        os.system('git config --global user.name \"DataLad Tester\"')\n        os.system('git config --global user.email \"test@example.com\"')\n        yield\n        if old_home:\n            os.environ[\"HOME\"] = old_home\n"
    ],
    "test_file_names": [
        "test_1.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Datalad installation attempts...\nVerifying installation...\nDatalad version: 0.13.0rc2.dev1\n✅ Installation successful!\nSuccessfully installed Datalad using Python 3.9\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 25 packages in 103ms\nInstalled 25 packages in 2.58s\n + coverage==7.6.10\n + docopt==0.6.2\n + exceptiongroup==1.2.2\n + grabbit==0.2.6\n + importlib-resources==6.4.5\n + iniconfig==2.0.0\n + nibabel==5.3.2\n + num2words==0.5.14\n + numpy==2.0.2\n + packaging==24.2\n + pandas==2.2.3\n + patsy==1.0.1\n + pluggy==1.5.0\n + pybids==0.6.5\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + python-dateutil==2.9.0.post0\n + pytz==2024.2\n + scipy==1.13.1\n + setuptools==75.6.0\n + six==1.17.0\n + tomli==2.2.1\n + typing-extensions==4.12.2\n + tzdata==2024.2\n + zipp==3.21.0\nResolved 63 packages in 3.59s\nPrepared 1 package in 5.09s\nInstalled 57 packages in 255ms\n + annexremote==1.6.6\n + appdirs==1.4.4\n + backports-tarfile==1.2.0\n + beautifulsoup4==4.12.3\n + boto==2.49.0\n + certifi==2024.12.14\n + cffi==1.17.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + citeproc-py==0.6.0\n + cryptography==44.0.0\n + datalad==0.13.0rc2 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_786968ef689c4a4c421da32d6eb71552fda7a4f6)\n + deprecated==1.2.15\n + distro==1.9.0\n + duecredit==0.10.2\n + exifread==3.0.0\n + fasteners==0.19\n + httpretty==1.1.4\n + humanize==4.11.0\n + idna==3.10\n + importlib-metadata==8.5.0\n + iso8601==2.1.0\n + jaraco-classes==3.4.0\n + jaraco-context==6.0.1\n + jaraco-functools==4.1.0\n + jeepney==0.8.0\n + jsmin==3.0.1\n + keyring==25.6.0\n + keyrings-alt==5.0.2\n + looseversion==1.3.0\n + lxml==5.3.0\n + more-itertools==10.5.0\n + msgpack==1.1.0\n + multidict==6.1.0\n + mutagen==1.47.0\n + nose==1.3.7\n + patool==1.15.0\n + pillow==11.0.0\n + propcache==0.2.1\n + pycparser==2.22\n + pygithub==2.5.0\n + pyjwt==2.10.1\n + pynacl==1.5.0\n + pyperclip==1.9.0\n + python-xmp-toolkit==2.0.2\n + pyyaml==6.0.2\n + requests==2.32.3\n + requests-ftp==0.3.1\n + secretstorage==3.3.3\n + simplejson==3.19.3\n + soupsieve==2.6\n + tqdm==4.67.1\n + urllib3==1.26.20\n + vcrpy==7.0.0\n + whoosh==2.7.4\n + wrapt==1.17.0\n + yarl==1.18.3\nResolved 97 packages in 195ms\nInstalled 32 packages in 347ms\n + alabaster==0.7.16\n + asv==0.6.4\n + asv-runner==0.2.1\n + babel==2.16.0\n + build==1.2.2.post1\n + distlib==0.3.9\n + docutils==0.21.2\n + filelock==3.16.1\n + gprof2dot==2024.6.6\n + imagesize==1.4.1\n + jinja2==3.1.5\n + json5==0.10.0\n + markupsafe==3.0.2\n + nose-timer==1.0.1\n + platformdirs==4.3.6\n + psutil==6.1.1\n + pygments==2.18.0\n + pympler==1.1\n + pypandoc==1.14\n + pyproject-hooks==1.2.0\n + snowballstemmer==2.2.0\n + sphinx==7.4.7\n + sphinx-rtd-theme==3.0.2\n + sphinxcontrib-applehelp==2.0.0\n + sphinxcontrib-devhelp==2.0.0\n + sphinxcontrib-htmlhelp==2.1.0\n + sphinxcontrib-jquery==4.1\n + sphinxcontrib-jsmath==1.0.1\n + sphinxcontrib-qthelp==2.0.0\n + sphinxcontrib-serializinghtml==2.0.0\n + tabulate==0.9.0\n + virtualenv==20.28.0\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_786968ef689c4a4c421da32d6eb71552fda7a4f6\nplugins: cov-6.0.0\ncollected 7 items\n\nr2e_tests/test_1.py ...x...                                              [100%]\n\n==================================== PASSES ====================================\n__________________ test_message_pbar_state_logging_is_demoted __________________\n----------------------------- Captured stdout call -----------------------------\ntesting 0\ntesting 1\n------------------------------ Captured log call -------------------------------\nINFO     dl-test:log.py:308 Clear progress bars\nINFO     dl-test:log.py:308 Refresh progress bars\nINFO     dl-test:log.py:308 Clear progress bars\nINFO     dl-test:log.py:308 Refresh progress bars\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_yesno\nPASSED r2e_tests/test_1.py::test_question_choices\nPASSED r2e_tests/test_1.py::test_hidden_doubleentry\nPASSED r2e_tests/test_1.py::test_IPythonUI\nPASSED r2e_tests/test_1.py::test_silent_question\nPASSED r2e_tests/test_1.py::test_message_pbar_state_logging_is_demoted\nXFAIL r2e_tests/test_1.py::test_progress_bar - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_progress_bar will be ignored\n========================= 6 passed, 1 xfailed in 5.54s =========================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_786968ef689c4a4c421da32d6eb71552fda7a4f6\nplugins: cov-6.0.0\ncollected 7 items\n\nr2e_tests/test_1.py ...x..F                                              [100%]\n\n=================================== FAILURES ===================================\n__________________ test_message_pbar_state_logging_is_demoted __________________\n\n    @patch(\"datalad.log.is_interactive\", lambda: False)\n    def test_message_pbar_state_logging_is_demoted():\n        from datalad.log import LoggerHelper\n    \n        name = \"dl-test\"\n        lgr = LoggerHelper(name).get_initialized_logger()\n        ui = ConsoleLog()\n    \n        with patch(\"datalad.ui.dialog.lgr\", lgr):\n            with swallow_logs(name=name, new_level=20) as cml:\n                ui.message(\"testing 0\")\n>               assert_not_in(\"Clear progress bars\", cml.out)\n\nr2e_tests/test_1.py:207: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:1111: in assertNotIn\n    self.fail(self._formatMessage(msg, standardMsg))\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <nose.tools.trivial.Dummy testMethod=nop>\nmsg = \"'Clear progress bars' unexpectedly found in '[INFO] Clear progress bars\\\\n[INFO] Refresh progress bars\\\\n'\"\n\n    def fail(self, msg=None):\n        \"\"\"Fail immediately, with the given message.\"\"\"\n>       raise self.failureException(msg)\nE       AssertionError: 'Clear progress bars' unexpectedly found in '[INFO] Clear progress bars\\n[INFO] Refresh progress bars\\n'\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:676: AssertionError\n----------------------------- Captured stdout call -----------------------------\ntesting 0\n------------------------------ Captured log call -------------------------------\nINFO     dl-test:log.py:308 Clear progress bars\nINFO     dl-test:log.py:308 Refresh progress bars\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_yesno\nPASSED r2e_tests/test_1.py::test_question_choices\nPASSED r2e_tests/test_1.py::test_hidden_doubleentry\nPASSED r2e_tests/test_1.py::test_IPythonUI\nPASSED r2e_tests/test_1.py::test_silent_question\nXFAIL r2e_tests/test_1.py::test_progress_bar - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_progress_bar will be ignored\nFAILED r2e_tests/test_1.py::test_message_pbar_state_logging_is_demoted - Asse...\n==================== 1 failed, 5 passed, 1 xfailed in 1.72s ====================\n",
    "old_commit_res_stderr": ""
}
datalad/ui/dialog.py datalad/ui/tests/test_dialog.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Non-Interactive Mode Displays Unnecessary Progress Messages

**Description:**
When running operations in non-interactive mode, invoking the `message()` function results in cluttered output with irrelevant progress messages such as "Clear progress bars" and "Refresh progress bars."

**Example Code:**
```python
from datalad.ui.dialog import ConsoleLog

ui = ConsoleLog()
ui.message("Starting data processing...")
```

**Expected Behavior:**
In non-interactive environments, the `message()` function should suppress progress-related messages by default to provide a clean and relevant output to the user.

**Actual Behavior:**
Progress messages like "Clear progress bars" and "Refresh progress bars" are displayed at the INFO logging level, leading to unnecessary and cluttered output even in non-interactive settings.
[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/ui/dialog.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Basic dialog-like interface for interactions in the terminal window

"""

__docformat__ = 'restructuredtext'

from logging import getLogger
lgr = getLogger('datalad.ui.dialog')

lgr.log(5, "Starting importing ui.dialog")

import os
import sys
import time

import getpass

#!!! OPT adds >100ms to import time!!!
# from unittest.mock import patch
from collections import deque
from copy import copy

from ..utils import auto_repr
from ..utils import on_windows
from .base import InteractiveUI
from ..dochelpers import exc_str

# Example APIs which might be useful to look for "inspiration"
#  man debconf-devel
#  man zenity
#
# "Fancy" output of progress etc in the terminal:
# - docker has multiple simultaneous progressbars.  Apparently "navigation"
#   is obtained with escape characters in the terminal.
#   see docker/pkg/jsonmessage/jsonmessage.go or following snippet
#
#from time import sleep
#import sys
#
#out = sys.stderr
#for i in range(10):
#  diff = 2
#  if i:
#      out.write("%c[%dA" % (27, diff))
#  out.write("%d\n%d\n" % (i, i ** 2))
#  sleep(0.5)
#
# They also use JSON representation for the message which might provide a nice abstraction
# Other useful codes
#         // <ESC>[2K = erase entire current line
#        fmt.Fprintf(out, "%c[2K\r", 27)
# and code in docker: pkg/progressreader/progressreader.go pkg/streamformatter/streamformatter.go
#
# reference for ESC codes: http://ascii-table.com/ansi-escape-sequences.php


@auto_repr
class ConsoleLog(object):

    progressbars = None

    def __init__(self, out=sys.stdout):
        self.out = out

    def message(self, msg, cr='\n'):
        from datalad.log import log_progress
        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')
        self.out.write(msg)
        if cr:
            self.out.write(cr)
        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')

    def error(self, error):
        self.out.write("ERROR: %s\n" % error)

    def get_progressbar(self, *args, **kwargs):
        """Return a progressbar.  See e.g. `tqdmProgressBar` about the interface

        Additional parameter is backend to choose among available
        """
        backend = kwargs.pop('backend', None)
        # Delay imports of progressbars until actually needed
        if ConsoleLog.progressbars is None:
            from .progressbars import progressbars
            ConsoleLog.progressbars = progressbars
        else:
            progressbars = ConsoleLog.progressbars

        if backend is None:
            # Resort to the configuration
            from .. import cfg
            backend = cfg.get('datalad.ui.progressbar', None)

        if backend is None:
            try:
                pbar = progressbars['tqdm']
            except KeyError:
                pbar = progressbars.values()[0]  # any
        else:
            pbar = progressbars[backend]
        return pbar(*args, out=self.out, **kwargs)

    @property
    def is_interactive(self):
        return isinstance(self, InteractiveUI)


@auto_repr
class SilentConsoleLog(ConsoleLog):
    """A ConsoleLog with a SilentProgressbar"""

    def get_progressbar(self, *args, **kwargs):
        from .progressbars import SilentProgressBar
        return SilentProgressBar(*args, **kwargs)

    def question(self, text, title=None, **kwargs):
        msg = "A non-interactive silent UI was asked for a response to a question: %s." % text
        if title is not None:
            msg += ' Title: %s.' % title
        if not kwargs.get('hidden'):
            kwargs_str = ', '.join(
                ('%s=%r' % (k, v)
                for k, v in kwargs.items()
                if v is not None))
            if kwargs_str:
                msg += " Additional arguments: %s" % kwargs_str
        else:
            msg += " Additional arguments are not shown because 'hidden' is set."
        raise RuntimeError(msg)


@auto_repr
class QuietConsoleLog(ConsoleLog):
    """A ConsoleLog with a LogProgressbar"""

    def get_progressbar(self, *args, **kwargs):
        from .progressbars import LogProgressBar
        return LogProgressBar(*args, **kwargs)


def getpass_echo(prompt='Password', stream=None):
    """Q&D workaround until we have proper 'centralized' UI -- just use getpass BUT enable echo
    """
    if on_windows:
        # Can't do anything fancy yet, so just ask the one without echo
        return getpass.getpass(prompt=prompt, stream=stream)
    else:
        # We can mock patch termios so that ECHO is not turned OFF.
        # Side-effect -- additional empty line is printed

        # def _no_emptyline_write(out):
        #     # Additional mock to prevent not needed empty line print since we do have echo
        #     # doesn't work since we don't know the stream here really
        #     if out == '\n':
        #         return
        #     stream.write(out)
        from unittest.mock import patch
        with patch('termios.ECHO', 255 ** 2):
            #patch.object(stream, 'write', _no_emptyline_write(stream)):
            return getpass.getpass(prompt=prompt, stream=stream)


def _get_value(value, hidden):
    return "<hidden>" if hidden else value


@auto_repr
class DialogUI(ConsoleLog, InteractiveUI):

    def __init__(self, *args, **kwargs):
        super(DialogUI, self).__init__(*args, **kwargs)
        # ATM doesn't make sense to print the same title for subsequent questions
        # so we will store previous one and not show it if was the previous one shown
        # within 5 seconds from prev question
        self._prev_title = None
        self._prev_title_time = 0

    def input(self, prompt, hidden=False):
        """Request user input

        Parameters
        ----------
        prompt: str
          Prompt for the entry
        """
        # if not hidden:
        #     self.out.write(msg + ": ")
        #     self.out.flush()  # not effective for stderr for some reason under annex
        #
        #     # TODO: raw_input works only if stdin was not controlled by
        #     # (e.g. if coming from annex).  So we might need to do the
        #     # same trick as get_pass() does while directly dealing with /dev/pty
        #     # and provide per-OS handling with stdin being override
        #     response = (raw_input if PY2 else input)()
        # else:
        return (getpass.getpass if hidden else getpass_echo)(prompt)

    def question(self, text,
                 title=None, choices=None,
                 default=None,
                 hidden=False,
                 repeat=None):
        # Do initial checks first
        if default and choices and default not in choices:
            raise ValueError("default value %r is not among choices: %s"
                             % (_get_value(default, hidden), choices))

        msg = ''
        if title and not (title == self._prev_title and time.time() - self._prev_title_time < 5):
            # might not actually get displayed if all in/out redirected
            # self.out.write(title + "\n")
            # so merge into msg for getpass
            msg += title + os.linesep

        def mark_default(x):
            return "[%s]" % x \
                if default is not None and x == default \
                else x

        if choices is not None:
            msg += "%s (choices: %s)" % (text, ', '.join(map(mark_default, choices)))
        elif default is not None:
            msg += '{} [{}]'.format(text, default)
        else:
            msg += text
        # Like this:
        #Anaconda format:
        #
        #Question? [choice1|choice2]
        #[default] >>> yes
        attempt = 0
        while True:
            attempt += 1
            if attempt >= 100:
                raise RuntimeError("This is 100th attempt. Something really went wrong")

            response = self.input("{}: ".format(msg), hidden=hidden)
            # TODO: dedicated option?  got annoyed by this one
            # multiple times already, typically we are not defining
            # new credentials where repetition would be needed.
            if hidden and repeat is None:
                repeat = hidden and choices is None

            if repeat:
                response_r = self.input('{} (repeat): '.format(msg), hidden=hidden)
                if response != response_r:
                    self.error("input mismatch, please start over")
                    continue

            if response and '\x03' in response:
                # Ctrl-C is part of the response -> clearly we should not pretend it's all good
                raise KeyboardInterrupt

            if not response and default:
                response = default
                break

            if choices and response not in choices:
                self.error("%r is not among choices: %s. Repeat your answer"
                           % (_get_value(response, hidden), choices))
                continue
            break

        self._prev_title = title
        self._prev_title_time = time.time()

        return response


class IPythonUI(DialogUI):
    """Custom to IPython frontend UI implementation

    There is no way to discriminate between web notebook or qt console,
    so we have just a single class for all.

    TODO: investigate how to provide 'proper' displays for
    IPython of progress bars so backend could choose the
    appropriate one

    """

    _tqdm_frontend = "unknown"

    def input(self, prompt, hidden=False):
        # We cannot and probably do not need to "abuse" termios
        if not hidden:
            self.out.write(prompt)
            self.out.flush()
            return input()
        else:
            return getpass.getpass(prompt=prompt)

    def get_progressbar(self, *args, **kwargs):
        """Return a progressbar.  See e.g. `tqdmProgressBar` about the
        interface

        Additional parameter is backend to choose among available
        """
        backend = kwargs.pop('backend', None)
        if self._tqdm_frontend == "unknown":
            from .progressbars import tqdmProgressBar
            try:
                from tqdm import tqdm_notebook  # check if available etc
                self.__class__._tqdm_frontend = 'ipython'
            except Exception as exc:
                lgr.warning(
                    "Regular progressbar will be used -- cannot import tqdm_notebook: %s",
                    exc_str(exc)
                )
                self.__class__._tqdm_frontend = None
        if self._tqdm_frontend:
            kwargs.update()
        return super(IPythonUI, self).get_progressbar(
                *args, frontend=self._tqdm_frontend, **kwargs)


# poor man thingie for now
@auto_repr
class UnderAnnexUI(DialogUI):
    def __init__(self, specialremote=None, **kwargs):
        if 'out' not in kwargs:
            # to avoid buffering
            # http://stackoverflow.com/a/181654/1265472
            #kwargs['out'] = os.fdopen(sys.stderr.fileno(), 'w', 0)
            # but wasn't effective! sp kist straogjt for now
            kwargs['out'] = sys.stderr
        super(UnderAnnexUI, self).__init__(**kwargs)
        self.specialremote = specialremote

    def set_specialremote(self, specialremote):
        self.specialremote = specialremote

    def get_progressbar(self, *args, **kwargs):
        if self.specialremote:
            kwargs = kwargs.copy()
            kwargs['backend'] = 'annex-remote'
            kwargs['remote'] = self.specialremote
        return super(UnderAnnexUI, self).get_progressbar(
                *args, **kwargs)


@auto_repr
class UnderTestsUI(DialogUI):
    """UI to help with testing functionality requiring interaction

    It will provide additional method to push responses to be provided,
    and could be used as a context manager
    """

    def __init__(self, **kwargs):
        super(UnderTestsUI, self).__init__(**kwargs)
        self._responses = deque()

    # TODO: possibly allow to provide expected messages etc, so we could
    # test that those are the actual ones which were given
    def add_responses(self, responses):
        if not isinstance(responses, (list, tuple)):
            responses = [responses]
        self._responses += list(responses)
        return self  # so we could use it as a context manager

    def get_responses(self):
        return self._responses

    def clear_responses(self):
        self._responses = deque()

    def question(self, *args, **kwargs):
        if not self._responses:
            raise AssertionError(
                "We are asked for a response whenever none is left to give"
            )
        return self._responses.popleft()

    # Context manager mode of operation which would also verify that
    # no responses left upon exiting
    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        responses = copy(self._responses)
        # we should clear the state so there is no side-effect
        self.clear_responses()
        assert not len(responses), \
            "Still have some responses left: %s" % repr(self._responses)

lgr.log(5, "Done importing ui.dialog")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Non-Interactive Mode Displays Unnecessary Progress Messages

**Description:**
When running operations in non-interactive mode, invoking the `message()` function results in cluttered output with irrelevant progress messages such as "Clear progress bars" and "Refresh progress bars."

**Example Code:**
```python
from datalad.ui.dialog import ConsoleLog

ui = ConsoleLog()
ui.message("Starting data processing...")
```

**Expected Behavior:**
In non-interactive environments, the `message()` function should suppress progress-related messages by default to provide a clean and relevant output to the user.

**Actual Behavior:**
Progress messages like "Clear progress bars" and "Refresh progress bars" are displayed at the INFO logging level, leading to unnecessary and cluttered output even in non-interactive settings.
[/ISSUE]
{
    "test_yesno": "PASSED",
    "test_question_choices": "PASSED",
    "test_hidden_doubleentry": "PASSED",
    "test_IPythonUI": "PASSED",
    "test_silent_question": "PASSED",
    "test_message_pbar_state_logging_is_demoted": "PASSED"
}
diff --git a/datalad/ui/dialog.py b/datalad/ui/dialog.py
index cf86ecdba..7bed97683 100644
--- a/datalad/ui/dialog.py
+++ b/datalad/ui/dialog.py
@@ -72,11 +72,13 @@ class ConsoleLog(object):
 
     def message(self, msg, cr='\n'):
         from datalad.log import log_progress
-        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')
+        log_progress(lgr.info, None, 'Clear progress bars', maint='clear',
+                     noninteractive_level=5)
         self.out.write(msg)
         if cr:
             self.out.write(cr)
-        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')
+        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh',
+                     noninteractive_level=5)
 
     def error(self, error):
         self.out.write("ERROR: %s\n" % error)

{"datalad/ui/dialog.py": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Basic dialog-like interface for interactions in the terminal window\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom logging import getLogger\nlgr = getLogger('datalad.ui.dialog')\n\nlgr.log(5, \"Starting importing ui.dialog\")\n\nimport os\nimport sys\nimport time\n\nimport getpass\n\n#!!! OPT adds >100ms to import time!!!\n# from unittest.mock import patch\nfrom collections import deque\nfrom copy import copy\n\nfrom ..utils import auto_repr\nfrom ..utils import on_windows\nfrom .base import InteractiveUI\nfrom ..dochelpers import exc_str\n\n# Example APIs which might be useful to look for \"inspiration\"\n#  man debconf-devel\n#  man zenity\n#\n# \"Fancy\" output of progress etc in the terminal:\n# - docker has multiple simultaneous progressbars.  Apparently \"navigation\"\n#   is obtained with escape characters in the terminal.\n#   see docker/pkg/jsonmessage/jsonmessage.go or following snippet\n#\n#from time import sleep\n#import sys\n#\n#out = sys.stderr\n#for i in range(10):\n#  diff = 2\n#  if i:\n#      out.write(\"%c[%dA\" % (27, diff))\n#  out.write(\"%d\\n%d\\n\" % (i, i ** 2))\n#  sleep(0.5)\n#\n# They also use JSON representation for the message which might provide a nice abstraction\n# Other useful codes\n#         // <ESC>[2K = erase entire current line\n#        fmt.Fprintf(out, \"%c[2K\\r\", 27)\n# and code in docker: pkg/progressreader/progressreader.go pkg/streamformatter/streamformatter.go\n#\n# reference for ESC codes: http://ascii-table.com/ansi-escape-sequences.php\n\n\n@auto_repr\nclass ConsoleLog(object):\n\n    progressbars = None\n\n    def __init__(self, out=sys.stdout):\n        self.out = out\n\n    def message(self, msg, cr='\\n'):\n        from datalad.log import log_progress\n        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')\n        self.out.write(msg)\n        if cr:\n            self.out.write(cr)\n        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')\n\n    def error(self, error):\n        self.out.write(\"ERROR: %s\\n\" % error)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        # Delay imports of progressbars until actually needed\n        if ConsoleLog.progressbars is None:\n            from .progressbars import progressbars\n            ConsoleLog.progressbars = progressbars\n        else:\n            progressbars = ConsoleLog.progressbars\n\n        if backend is None:\n            # Resort to the configuration\n            from .. import cfg\n            backend = cfg.get('datalad.ui.progressbar', None)\n\n        if backend is None:\n            try:\n                pbar = progressbars['tqdm']\n            except KeyError:\n                pbar = progressbars.values()[0]  # any\n        else:\n            pbar = progressbars[backend]\n        return pbar(*args, out=self.out, **kwargs)\n\n    @property\n    def is_interactive(self):\n        return isinstance(self, InteractiveUI)\n\n\n@auto_repr\nclass SilentConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a SilentProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import SilentProgressBar\n        return SilentProgressBar(*args, **kwargs)\n\n    def question(self, text, title=None, **kwargs):\n        msg = \"A non-interactive silent UI was asked for a response to a question: %s.\" % text\n        if title is not None:\n            msg += ' Title: %s.' % title\n        if not kwargs.get('hidden'):\n            kwargs_str = ', '.join(\n                ('%s=%r' % (k, v)\n                for k, v in kwargs.items()\n                if v is not None))\n            if kwargs_str:\n                msg += \" Additional arguments: %s\" % kwargs_str\n        else:\n            msg += \" Additional arguments are not shown because 'hidden' is set.\"\n        raise RuntimeError(msg)\n\n\n@auto_repr\nclass QuietConsoleLog(ConsoleLog):\n    \"\"\"A ConsoleLog with a LogProgressbar\"\"\"\n\n    def get_progressbar(self, *args, **kwargs):\n        from .progressbars import LogProgressBar\n        return LogProgressBar(*args, **kwargs)\n\n\ndef getpass_echo(prompt='Password', stream=None):\n    \"\"\"Q&D workaround until we have proper 'centralized' UI -- just use getpass BUT enable echo\n    \"\"\"\n    if on_windows:\n        # Can't do anything fancy yet, so just ask the one without echo\n        return getpass.getpass(prompt=prompt, stream=stream)\n    else:\n        # We can mock patch termios so that ECHO is not turned OFF.\n        # Side-effect -- additional empty line is printed\n\n        # def _no_emptyline_write(out):\n        #     # Additional mock to prevent not needed empty line print since we do have echo\n        #     # doesn't work since we don't know the stream here really\n        #     if out == '\\n':\n        #         return\n        #     stream.write(out)\n        from unittest.mock import patch\n        with patch('termios.ECHO', 255 ** 2):\n            #patch.object(stream, 'write', _no_emptyline_write(stream)):\n            return getpass.getpass(prompt=prompt, stream=stream)\n\n\ndef _get_value(value, hidden):\n    return \"<hidden>\" if hidden else value\n\n\n@auto_repr\nclass DialogUI(ConsoleLog, InteractiveUI):\n\n    def __init__(self, *args, **kwargs):\n        super(DialogUI, self).__init__(*args, **kwargs)\n        # ATM doesn't make sense to print the same title for subsequent questions\n        # so we will store previous one and not show it if was the previous one shown\n        # within 5 seconds from prev question\n        self._prev_title = None\n        self._prev_title_time = 0\n\n    def input(self, prompt, hidden=False):\n        \"\"\"Request user input\n\n        Parameters\n        ----------\n        prompt: str\n          Prompt for the entry\n        \"\"\"\n        # if not hidden:\n        #     self.out.write(msg + \": \")\n        #     self.out.flush()  # not effective for stderr for some reason under annex\n        #\n        #     # TODO: raw_input works only if stdin was not controlled by\n        #     # (e.g. if coming from annex).  So we might need to do the\n        #     # same trick as get_pass() does while directly dealing with /dev/pty\n        #     # and provide per-OS handling with stdin being override\n        #     response = (raw_input if PY2 else input)()\n        # else:\n        return (getpass.getpass if hidden else getpass_echo)(prompt)\n\n    def question(self, text,\n                 title=None, choices=None,\n                 default=None,\n                 hidden=False,\n                 repeat=None):\n        # Do initial checks first\n        if default and choices and default not in choices:\n            raise ValueError(\"default value %r is not among choices: %s\"\n                             % (_get_value(default, hidden), choices))\n\n        msg = ''\n        if title and not (title == self._prev_title and time.time() - self._prev_title_time < 5):\n            # might not actually get displayed if all in/out redirected\n            # self.out.write(title + \"\\n\")\n            # so merge into msg for getpass\n            msg += title + os.linesep\n\n        def mark_default(x):\n            return \"[%s]\" % x \\\n                if default is not None and x == default \\\n                else x\n\n        if choices is not None:\n            msg += \"%s (choices: %s)\" % (text, ', '.join(map(mark_default, choices)))\n        elif default is not None:\n            msg += '{} [{}]'.format(text, default)\n        else:\n            msg += text\n        # Like this:\n        #Anaconda format:\n        #\n        #Question? [choice1|choice2]\n        #[default] >>> yes\n        attempt = 0\n        while True:\n            attempt += 1\n            if attempt >= 100:\n                raise RuntimeError(\"This is 100th attempt. Something really went wrong\")\n\n            response = self.input(\"{}: \".format(msg), hidden=hidden)\n            # TODO: dedicated option?  got annoyed by this one\n            # multiple times already, typically we are not defining\n            # new credentials where repetition would be needed.\n            if hidden and repeat is None:\n                repeat = hidden and choices is None\n\n            if repeat:\n                response_r = self.input('{} (repeat): '.format(msg), hidden=hidden)\n                if response != response_r:\n                    self.error(\"input mismatch, please start over\")\n                    continue\n\n            if response and '\\x03' in response:\n                # Ctrl-C is part of the response -> clearly we should not pretend it's all good\n                raise KeyboardInterrupt\n\n            if not response and default:\n                response = default\n                break\n\n            if choices and response not in choices:\n                self.error(\"%r is not among choices: %s. Repeat your answer\"\n                           % (_get_value(response, hidden), choices))\n                continue\n            break\n\n        self._prev_title = title\n        self._prev_title_time = time.time()\n\n        return response\n\n\nclass IPythonUI(DialogUI):\n    \"\"\"Custom to IPython frontend UI implementation\n\n    There is no way to discriminate between web notebook or qt console,\n    so we have just a single class for all.\n\n    TODO: investigate how to provide 'proper' displays for\n    IPython of progress bars so backend could choose the\n    appropriate one\n\n    \"\"\"\n\n    _tqdm_frontend = \"unknown\"\n\n    def input(self, prompt, hidden=False):\n        # We cannot and probably do not need to \"abuse\" termios\n        if not hidden:\n            self.out.write(prompt)\n            self.out.flush()\n            return input()\n        else:\n            return getpass.getpass(prompt=prompt)\n\n    def get_progressbar(self, *args, **kwargs):\n        \"\"\"Return a progressbar.  See e.g. `tqdmProgressBar` about the\n        interface\n\n        Additional parameter is backend to choose among available\n        \"\"\"\n        backend = kwargs.pop('backend', None)\n        if self._tqdm_frontend == \"unknown\":\n            from .progressbars import tqdmProgressBar\n            try:\n                from tqdm import tqdm_notebook  # check if available etc\n                self.__class__._tqdm_frontend = 'ipython'\n            except Exception as exc:\n                lgr.warning(\n                    \"Regular progressbar will be used -- cannot import tqdm_notebook: %s\",\n                    exc_str(exc)\n                )\n                self.__class__._tqdm_frontend = None\n        if self._tqdm_frontend:\n            kwargs.update()\n        return super(IPythonUI, self).get_progressbar(\n                *args, frontend=self._tqdm_frontend, **kwargs)\n\n\n# poor man thingie for now\n@auto_repr\nclass UnderAnnexUI(DialogUI):\n    def __init__(self, specialremote=None, **kwargs):\n        if 'out' not in kwargs:\n            # to avoid buffering\n            # http://stackoverflow.com/a/181654/1265472\n            #kwargs['out'] = os.fdopen(sys.stderr.fileno(), 'w', 0)\n            # but wasn't effective! sp kist straogjt for now\n            kwargs['out'] = sys.stderr\n        super(UnderAnnexUI, self).__init__(**kwargs)\n        self.specialremote = specialremote\n\n    def set_specialremote(self, specialremote):\n        self.specialremote = specialremote\n\n    def get_progressbar(self, *args, **kwargs):\n        if self.specialremote:\n            kwargs = kwargs.copy()\n            kwargs['backend'] = 'annex-remote'\n            kwargs['remote'] = self.specialremote\n        return super(UnderAnnexUI, self).get_progressbar(\n                *args, **kwargs)\n\n\n@auto_repr\nclass UnderTestsUI(DialogUI):\n    \"\"\"UI to help with testing functionality requiring interaction\n\n    It will provide additional method to push responses to be provided,\n    and could be used as a context manager\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        super(UnderTestsUI, self).__init__(**kwargs)\n        self._responses = deque()\n\n    # TODO: possibly allow to provide expected messages etc, so we could\n    # test that those are the actual ones which were given\n    def add_responses(self, responses):\n        if not isinstance(responses, (list, tuple)):\n            responses = [responses]\n        self._responses += list(responses)\n        return self  # so we could use it as a context manager\n\n    def get_responses(self):\n        return self._responses\n\n    def clear_responses(self):\n        self._responses = deque()\n\n    def question(self, *args, **kwargs):\n        if not self._responses:\n            raise AssertionError(\n                \"We are asked for a response whenever none is left to give\"\n            )\n        return self._responses.popleft()\n\n    # Context manager mode of operation which would also verify that\n    # no responses left upon exiting\n    def __enter__(self):\n        pass\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        responses = copy(self._responses)\n        # we should clear the state so there is no side-effect\n        self.clear_responses()\n        assert not len(responses), \\\n            \"Still have some responses left: %s\" % repr(self._responses)\n\nlgr.log(5, \"Done importing ui.dialog\")\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Non-Interactive Mode Displays Unnecessary Progress Messages

**Description:**
When running operations in non-interactive mode, invoking the `message()` function results in cluttered output with irrelevant progress messages such as "Clear progress bars" and "Refresh progress bars."

**Example Code:**
```python
from datalad.ui.dialog import ConsoleLog

ui = ConsoleLog()
ui.message("Starting data processing...")
```

**Expected Behavior:**
In non-interactive environments, the `message()` function should suppress progress-related messages by default to provide a clean and relevant output to the user.

**Actual Behavior:**
Progress messages like "Clear progress bars" and "Refresh progress bars" are displayed at the INFO logging level, leading to unnecessary and cluttered output even in non-interactive settings.
[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/ui/dialog.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Basic dialog-like interface for interactions in the terminal window

"""

__docformat__ = 'restructuredtext'

from logging import getLogger
lgr = getLogger('datalad.ui.dialog')

lgr.log(5, "Starting importing ui.dialog")

import os
import sys
import time

import getpass

#!!! OPT adds >100ms to import time!!!
# from unittest.mock import patch
from collections import deque
from copy import copy

from ..utils import auto_repr
from ..utils import on_windows
from .base import InteractiveUI
from ..dochelpers import exc_str

# Example APIs which might be useful to look for "inspiration"
#  man debconf-devel
#  man zenity
#
# "Fancy" output of progress etc in the terminal:
# - docker has multiple simultaneous progressbars.  Apparently "navigation"
#   is obtained with escape characters in the terminal.
#   see docker/pkg/jsonmessage/jsonmessage.go or following snippet
#
#from time import sleep
#import sys
#
#out = sys.stderr
#for i in range(10):
#  diff = 2
#  if i:
#      out.write("%c[%dA" % (27, diff))
#  out.write("%d\n%d\n" % (i, i ** 2))
#  sleep(0.5)
#
# They also use JSON representation for the message which might provide a nice abstraction
# Other useful codes
#         // <ESC>[2K = erase entire current line
#        fmt.Fprintf(out, "%c[2K\r", 27)
# and code in docker: pkg/progressreader/progressreader.go pkg/streamformatter/streamformatter.go
#
# reference for ESC codes: http://ascii-table.com/ansi-escape-sequences.php


@auto_repr
class ConsoleLog(object):

    progressbars = None

    def __init__(self, out=sys.stdout):
        self.out = out

    def message(self, msg, cr='\n'):
        from datalad.log import log_progress
        log_progress(lgr.info, None, 'Clear progress bars', maint='clear')
        self.out.write(msg)
        if cr:
            self.out.write(cr)
        log_progress(lgr.info, None, 'Refresh progress bars', maint='refresh')

    def error(self, error):
        self.out.write("ERROR: %s\n" % error)

    def get_progressbar(self, *args, **kwargs):
        """Return a progressbar.  See e.g. `tqdmProgressBar` about the interface

        Additional parameter is backend to choose among available
        """
        backend = kwargs.pop('backend', None)
        # Delay imports of progressbars until actually needed
        if ConsoleLog.progressbars is None:
            from .progressbars import progressbars
            ConsoleLog.progressbars = progressbars
        else:
            progressbars = ConsoleLog.progressbars

        if backend is None:
            # Resort to the configuration
            from .. import cfg
            backend = cfg.get('datalad.ui.progressbar', None)

        if backend is None:
            try:
                pbar = progressbars['tqdm']
            except KeyError:
                pbar = progressbars.values()[0]  # any
        else:
            pbar = progressbars[backend]
        return pbar(*args, out=self.out, **kwargs)

    @property
    def is_interactive(self):
        return isinstance(self, InteractiveUI)


@auto_repr
class SilentConsoleLog(ConsoleLog):
    """A ConsoleLog with a SilentProgressbar"""

    def get_progressbar(self, *args, **kwargs):
        from .progressbars import SilentProgressBar
        return SilentProgressBar(*args, **kwargs)

    def question(self, text, title=None, **kwargs):
        msg = "A non-interactive silent UI was asked for a response to a question: %s." % text
        if title is not None:
            msg += ' Title: %s.' % title
        if not kwargs.get('hidden'):
            kwargs_str = ', '.join(
                ('%s=%r' % (k, v)
                for k, v in kwargs.items()
                if v is not None))
            if kwargs_str:
                msg += " Additional arguments: %s" % kwargs_str
        else:
            msg += " Additional arguments are not shown because 'hidden' is set."
        raise RuntimeError(msg)


@auto_repr
class QuietConsoleLog(ConsoleLog):
    """A ConsoleLog with a LogProgressbar"""

    def get_progressbar(self, *args, **kwargs):
        from .progressbars import LogProgressBar
        return LogProgressBar(*args, **kwargs)


def getpass_echo(prompt='Password', stream=None):
    """Q&D workaround until we have proper 'centralized' UI -- just use getpass BUT enable echo
    """
    if on_windows:
        # Can't do anything fancy yet, so just ask the one without echo
        return getpass.getpass(prompt=prompt, stream=stream)
    else:
        # We can mock patch termios so that ECHO is not turned OFF.
        # Side-effect -- additional empty line is printed

        # def _no_emptyline_write(out):
        #     # Additional mock to prevent not needed empty line print since we do have echo
        #     # doesn't work since we don't know the stream here really
        #     if out == '\n':
        #         return
        #     stream.write(out)
        from unittest.mock import patch
        with patch('termios.ECHO', 255 ** 2):
            #patch.object(stream, 'write', _no_emptyline_write(stream)):
            return getpass.getpass(prompt=prompt, stream=stream)


def _get_value(value, hidden):
    return "<hidden>" if hidden else value


@auto_repr
class DialogUI(ConsoleLog, InteractiveUI):

    def __init__(self, *args, **kwargs):
        super(DialogUI, self).__init__(*args, **kwargs)
        # ATM doesn't make sense to print the same title for subsequent questions
        # so we will store previous one and not show it if was the previous one shown
        # within 5 seconds from prev question
        self._prev_title = None
        self._prev_title_time = 0

    def input(self, prompt, hidden=False):
        """Request user input

        Parameters
        ----------
        prompt: str
          Prompt for the entry
        """
        # if not hidden:
        #     self.out.write(msg + ": ")
        #     self.out.flush()  # not effective for stderr for some reason under annex
        #
        #     # TODO: raw_input works only if stdin was not controlled by
        #     # (e.g. if coming from annex).  So we might need to do the
        #     # same trick as get_pass() does while directly dealing with /dev/pty
        #     # and provide per-OS handling with stdin being override
        #     response = (raw_input if PY2 else input)()
        # else:
        return (getpass.getpass if hidden else getpass_echo)(prompt)

    def question(self, text,
                 title=None, choices=None,
                 default=None,
                 hidden=False,
                 repeat=None):
        # Do initial checks first
        if default and choices and default not in choices:
            raise ValueError("default value %r is not among choices: %s"
                             % (_get_value(default, hidden), choices))

        msg = ''
        if title and not (title == self._prev_title and time.time() - self._prev_title_time < 5):
            # might not actually get displayed if all in/out redirected
            # self.out.write(title + "\n")
            # so merge into msg for getpass
            msg += title + os.linesep

        def mark_default(x):
            return "[%s]" % x \
                if default is not None and x == default \
                else x

        if choices is not None:
            msg += "%s (choices: %s)" % (text, ', '.join(map(mark_default, choices)))
        elif default is not None:
            msg += '{} [{}]'.format(text, default)
        else:
            msg += text
        # Like this:
        #Anaconda format:
        #
        #Question? [choice1|choice2]
        #[default] >>> yes
        attempt = 0
        while True:
            attempt += 1
            if attempt >= 100:
                raise RuntimeError("This is 100th attempt. Something really went wrong")

            response = self.input("{}: ".format(msg), hidden=hidden)
            # TODO: dedicated option?  got annoyed by this one
            # multiple times already, typically we are not defining
            # new credentials where repetition would be needed.
            if hidden and repeat is None:
                repeat = hidden and choices is None

            if repeat:
                response_r = self.input('{} (repeat): '.format(msg), hidden=hidden)
                if response != response_r:
                    self.error("input mismatch, please start over")
                    continue

            if response and '\x03' in response:
                # Ctrl-C is part of the response -> clearly we should not pretend it's all good
                raise KeyboardInterrupt

            if not response and default:
                response = default
                break

            if choices and response not in choices:
                self.error("%r is not among choices: %s. Repeat your answer"
                           % (_get_value(response, hidden), choices))
                continue
            break

        self._prev_title = title
        self._prev_title_time = time.time()

        return response


class IPythonUI(DialogUI):
    """Custom to IPython frontend UI implementation

    There is no way to discriminate between web notebook or qt console,
    so we have just a single class for all.

    TODO: investigate how to provide 'proper' displays for
    IPython of progress bars so backend could choose the
    appropriate one

    """

    _tqdm_frontend = "unknown"

    def input(self, prompt, hidden=False):
        # We cannot and probably do not need to "abuse" termios
        if not hidden:
            self.out.write(prompt)
            self.out.flush()
            return input()
        else:
            return getpass.getpass(prompt=prompt)

    def get_progressbar(self, *args, **kwargs):
        """Return a progressbar.  See e.g. `tqdmProgressBar` about the
        interface

        Additional parameter is backend to choose among available
        """
        backend = kwargs.pop('backend', None)
        if self._tqdm_frontend == "unknown":
            from .progressbars import tqdmProgressBar
            try:
                from tqdm import tqdm_notebook  # check if available etc
                self.__class__._tqdm_frontend = 'ipython'
            except Exception as exc:
                lgr.warning(
                    "Regular progressbar will be used -- cannot import tqdm_notebook: %s",
                    exc_str(exc)
                )
                self.__class__._tqdm_frontend = None
        if self._tqdm_frontend:
            kwargs.update()
        return super(IPythonUI, self).get_progressbar(
                *args, frontend=self._tqdm_frontend, **kwargs)


# poor man thingie for now
@auto_repr
class UnderAnnexUI(DialogUI):
    def __init__(self, specialremote=None, **kwargs):
        if 'out' not in kwargs:
            # to avoid buffering
            # http://stackoverflow.com/a/181654/1265472
            #kwargs['out'] = os.fdopen(sys.stderr.fileno(), 'w', 0)
            # but wasn't effective! sp kist straogjt for now
            kwargs['out'] = sys.stderr
        super(UnderAnnexUI, self).__init__(**kwargs)
        self.specialremote = specialremote

    def set_specialremote(self, specialremote):
        self.specialremote = specialremote

    def get_progressbar(self, *args, **kwargs):
        if self.specialremote:
            kwargs = kwargs.copy()
            kwargs['backend'] = 'annex-remote'
            kwargs['remote'] = self.specialremote
        return super(UnderAnnexUI, self).get_progressbar(
                *args, **kwargs)


@auto_repr
class UnderTestsUI(DialogUI):
    """UI to help with testing functionality requiring interaction

    It will provide additional method to push responses to be provided,
    and could be used as a context manager
    """

    def __init__(self, **kwargs):
        super(UnderTestsUI, self).__init__(**kwargs)
        self._responses = deque()

    # TODO: possibly allow to provide expected messages etc, so we could
    # test that those are the actual ones which were given
    def add_responses(self, responses):
        if not isinstance(responses, (list, tuple)):
            responses = [responses]
        self._responses += list(responses)
        return self  # so we could use it as a context manager

    def get_responses(self):
        return self._responses

    def clear_responses(self):
        self._responses = deque()

    def question(self, *args, **kwargs):
        if not self._responses:
            raise AssertionError(
                "We are asked for a response whenever none is left to give"
            )
        return self._responses.popleft()

    # Context manager mode of operation which would also verify that
    # no responses left upon exiting
    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        responses = copy(self._responses)
        # we should clear the state so there is no side-effect
        self.clear_responses()
        assert not len(responses), \
            "Still have some responses left: %s" % repr(self._responses)

lgr.log(5, "Done importing ui.dialog")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user