We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"pause" module fails with NameError when "curses" is not installed
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
The "pause" module fails with a NameError when the curses Python module is not available.

```
Traceback (most recent call last):
  File "/usr/local/python/bin/ansible-playbook", line 118, in <module>
    exit_code = cli.run()
  File "/usr/local/python/lib/python2.7/site-packages/ansible/cli/playbook.py", line 122, in run
    results = pbex.run()
  File "/usr/local/python/lib/python2.7/site-packages/ansible/executor/playbook_executor.py", line 159, in run
    result = self._tqm.run(play=play)
  File "/usr/local/python/lib/python2.7/site-packages/ansible/executor/task_queue_manager.py", line 289, in run
    play_return = strategy.run(iterator, play_context)
  File "/usr/local/python/lib/python2.7/site-packages/ansible/plugins/strategy/linear.py", line 247, in run
    action = action_loader.get(task.action, class_only=True)
  File "/usr/local/python/lib/python2.7/site-packages/ansible/plugins/loader.py", line 366, in get
    self._module_cache[path] = self._load_module_source(name, path)
  File "/usr/local/python/lib/python2.7/site-packages/ansible/plugins/loader.py", line 345, in _load_module_source
    module = imp.load_source(full_name, path, module_file)
  File "/usr/local/python/lib/python2.7/site-packages/ansible/plugins/action/pause.py", line 44, in <module>
    except (ImportError, curses.error):
NameError: name 'curses' is not defined
```

Caused by this code block introduced by https://github.com/ansible/ansible/pull/40134 to fix #35372:

```python
try:
    import curses
    curses.setupterm()
    HAS_CURSES = True
except (ImportError, curses.error):
    HAS_CURSES = False
```

A better fix would be to nest the curses.error try-catch within the block.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
plugins/action/pause.py

##### ANSIBLE VERSION
ansible 2.5.4
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/etc/ansible/modules']
  ansible python module location = /usr/local/python/lib/python2.7/site-packages/ansible
  executable location = /usr/local/python/bin/ansible
  python version = 2.7.10 (default, Oct 11 2015, 23:13:31) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### STEPS TO REPRODUCE
On a machine without "curses", run:

> $ ansible localhost -m pause -a 'seconds=1'
> ERROR! Unexpected Exception, this is probably a bug: name 'curses' is not defined
> to see the full traceback, use -vvv

##### EXPECTED RESULTS
N/A

##### ACTUAL RESULTS
See above.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/plugins/action/pause.py`
Content:
```
1 # Copyright 2012, Tim Bielawa <tbielawa@redhat.com>
2 #
3 # This file is part of Ansible
4 #
5 # Ansible is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Ansible is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17 from __future__ import (absolute_import, division, print_function)
18 __metaclass__ = type
19 
20 import datetime
21 import signal
22 import sys
23 import termios
24 import time
25 import tty
26 
27 from os import isatty
28 from ansible.errors import AnsibleError
29 from ansible.module_utils._text import to_text, to_native
30 from ansible.module_utils.parsing.convert_bool import boolean
31 from ansible.module_utils.six import PY3
32 from ansible.plugins.action import ActionBase
33 
34 try:
35     from __main__ import display
36 except ImportError:
37     from ansible.utils.display import Display
38     display = Display()
39 
40 try:
41     import curses
42     curses.setupterm()
43     HAS_CURSES = True
44 except (ImportError, curses.error):
45     HAS_CURSES = False
46 
47 if HAS_CURSES:
48     MOVE_TO_BOL = curses.tigetstr('cr')
49     CLEAR_TO_EOL = curses.tigetstr('el')
50 else:
51     MOVE_TO_BOL = b'\r'
52     CLEAR_TO_EOL = b'\x1b[K'
53 
54 
55 class AnsibleTimeoutExceeded(Exception):
56     pass
57 
58 
59 def timeout_handler(signum, frame):
60     raise AnsibleTimeoutExceeded
61 
62 
63 def clear_line(stdout):
64     stdout.write(b'\x1b[%s' % MOVE_TO_BOL)
65     stdout.write(b'\x1b[%s' % CLEAR_TO_EOL)
66 
67 
68 class ActionModule(ActionBase):
69     ''' pauses execution for a length or time, or until input is received '''
70 
71     PAUSE_TYPES = ['seconds', 'minutes', 'prompt', 'echo', '']
72     BYPASS_HOST_LOOP = True
73 
74     def run(self, tmp=None, task_vars=None):
75         ''' run the pause action module '''
76         if task_vars is None:
77             task_vars = dict()
78 
79         result = super(ActionModule, self).run(tmp, task_vars)
80         del tmp  # tmp no longer has any effect
81 
82         duration_unit = 'minutes'
83         prompt = None
84         seconds = None
85         echo = True
86         echo_prompt = ''
87         result.update(dict(
88             changed=False,
89             rc=0,
90             stderr='',
91             stdout='',
92             start=None,
93             stop=None,
94             delta=None,
95             echo=echo
96         ))
97 
98         if not set(self._task.args.keys()) <= set(self.PAUSE_TYPES):
99             result['failed'] = True
100             result['msg'] = "Invalid argument given. Must be one of: %s" % ", ".join(self.PAUSE_TYPES)
101             return result
102 
103         # Should keystrokes be echoed to stdout?
104         if 'echo' in self._task.args:
105             try:
106                 echo = boolean(self._task.args['echo'])
107             except TypeError as e:
108                 result['failed'] = True
109                 result['msg'] = to_native(e)
110                 return result
111 
112             # Add a note saying the output is hidden if echo is disabled
113             if not echo:
114                 echo_prompt = ' (output is hidden)'
115 
116         # Is 'prompt' a key in 'args'?
117         if 'prompt' in self._task.args:
118             prompt = "[%s]\n%s%s:" % (self._task.get_name().strip(), self._task.args['prompt'], echo_prompt)
119         else:
120             # If no custom prompt is specified, set a default prompt
121             prompt = "[%s]\n%s%s:" % (self._task.get_name().strip(), 'Press enter to continue, Ctrl+C to interrupt', echo_prompt)
122 
123         # Are 'minutes' or 'seconds' keys that exist in 'args'?
124         if 'minutes' in self._task.args or 'seconds' in self._task.args:
125             try:
126                 if 'minutes' in self._task.args:
127                     # The time() command operates in seconds so we need to
128                     # recalculate for minutes=X values.
129                     seconds = int(self._task.args['minutes']) * 60
130                 else:
131                     seconds = int(self._task.args['seconds'])
132                     duration_unit = 'seconds'
133 
134             except ValueError as e:
135                 result['failed'] = True
136                 result['msg'] = u"non-integer value given for prompt duration:\n%s" % to_text(e)
137                 return result
138 
139         ########################################################################
140         # Begin the hard work!
141 
142         start = time.time()
143         result['start'] = to_text(datetime.datetime.now())
144         result['user_input'] = b''
145 
146         fd = None
147         old_settings = None
148         try:
149             if seconds is not None:
150                 if seconds < 1:
151                     seconds = 1
152 
153                 # setup the alarm handler
154                 signal.signal(signal.SIGALRM, timeout_handler)
155                 signal.alarm(seconds)
156 
157                 # show the timer and control prompts
158                 display.display("Pausing for %d seconds%s" % (seconds, echo_prompt))
159                 display.display("(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)\r"),
160 
161                 # show the prompt specified in the task
162                 if 'prompt' in self._task.args:
163                     display.display(prompt)
164 
165             else:
166                 display.display(prompt)
167 
168             # save the attributes on the existing (duped) stdin so
169             # that we can restore them later after we set raw mode
170             fd = None
171             try:
172                 if PY3:
173                     stdin = self._connection._new_stdin.buffer
174                     stdout = sys.stdout.buffer
175                 else:
176                     stdin = self._connection._new_stdin
177                     stdout = sys.stdout
178                 fd = stdin.fileno()
179             except (ValueError, AttributeError):
180                 # ValueError: someone is using a closed file descriptor as stdin
181                 # AttributeError: someone is using a null file descriptor as stdin on windoez
182                 stdin = None
183 
184             if fd is not None:
185                 if isatty(fd):
186 
187                     # grab actual Ctrl+C sequence
188                     try:
189                         intr = termios.tcgetattr(fd)[6][termios.VINTR]
190                     except Exception:
191                         # unsupported/not present, use default
192                         intr = b'\x03'  # value for Ctrl+C
193 
194                     # get backspace sequences
195                     try:
196                         backspace = termios.tcgetattr(fd)[6][termios.VERASE]
197                     except Exception:
198                         backspace = [b'\x7f', b'\x08']
199 
200                     old_settings = termios.tcgetattr(fd)
201                     tty.setraw(fd)
202                     tty.setraw(stdout.fileno())
203 
204                     # Only echo input if no timeout is specified
205                     if not seconds and echo:
206                         new_settings = termios.tcgetattr(fd)
207                         new_settings[3] = new_settings[3] | termios.ECHO
208                         termios.tcsetattr(fd, termios.TCSANOW, new_settings)
209 
210                     # flush the buffer to make sure no previous key presses
211                     # are read in below
212                     termios.tcflush(stdin, termios.TCIFLUSH)
213 
214             while True:
215                 try:
216                     if fd is not None:
217                         key_pressed = stdin.read(1)
218                         if key_pressed == intr:  # value for Ctrl+C
219                             clear_line(stdout)
220                             raise KeyboardInterrupt
221 
222                     if not seconds:
223                         if fd is None or not isatty(fd):
224                             display.warning("Not waiting for response to prompt as stdin is not interactive")
225                             break
226 
227                         # read key presses and act accordingly
228                         if key_pressed in (b'\r', b'\n'):
229                             clear_line(stdout)
230                             break
231                         elif key_pressed in backspace:
232                             # delete a character if backspace is pressed
233                             result['user_input'] = result['user_input'][:-1]
234                             clear_line(stdout)
235                             if echo:
236                                 stdout.write(result['user_input'])
237                             stdout.flush()
238                         else:
239                             result['user_input'] += key_pressed
240 
241                 except KeyboardInterrupt:
242                     signal.alarm(0)
243                     display.display("Press 'C' to continue the play or 'A' to abort \r"),
244                     if self._c_or_a(stdin):
245                         clear_line(stdout)
246                         break
247 
248                     clear_line(stdout)
249 
250                     raise AnsibleError('user requested abort!')
251 
252         except AnsibleTimeoutExceeded:
253             # this is the exception we expect when the alarm signal
254             # fires, so we simply ignore it to move into the cleanup
255             pass
256         finally:
257             # cleanup and save some information
258             # restore the old settings for the duped stdin fd
259             if not(None in (fd, old_settings)) and isatty(fd):
260                 termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
261 
262             duration = time.time() - start
263             result['stop'] = to_text(datetime.datetime.now())
264             result['delta'] = int(duration)
265 
266             if duration_unit == 'minutes':
267                 duration = round(duration / 60.0, 2)
268             else:
269                 duration = round(duration, 2)
270             result['stdout'] = "Paused for %s %s" % (duration, duration_unit)
271 
272         result['user_input'] = to_text(result['user_input'], errors='surrogate_or_strict')
273         return result
274 
275     def _c_or_a(self, stdin):
276         while True:
277             key_pressed = stdin.read(1)
278             if key_pressed.lower() == b'a':
279                 return False
280             elif key_pressed.lower() == b'c':
281                 return True
282
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/plugins/action/pause.py b/lib/ansible/plugins/action/pause.py
--- a/lib/ansible/plugins/action/pause.py
+++ b/lib/ansible/plugins/action/pause.py
@@ -39,9 +39,14 @@
 
 try:
     import curses
-    curses.setupterm()
-    HAS_CURSES = True
-except (ImportError, curses.error):
+
+    # Nest the try except since curses.error is not available if curses did not import
+    try:
+        curses.setupterm()
+        HAS_CURSES = True
+    except curses.error:
+        HAS_CURSES = False
+except ImportError:
     HAS_CURSES = False
 
 if HAS_CURSES:

{"golden_diff": "diff --git a/lib/ansible/plugins/action/pause.py b/lib/ansible/plugins/action/pause.py\n--- a/lib/ansible/plugins/action/pause.py\n+++ b/lib/ansible/plugins/action/pause.py\n@@ -39,9 +39,14 @@\n \n try:\n     import curses\n-    curses.setupterm()\n-    HAS_CURSES = True\n-except (ImportError, curses.error):\n+\n+    # Nest the try except since curses.error is not available if curses did not import\n+    try:\n+        curses.setupterm()\n+        HAS_CURSES = True\n+    except curses.error:\n+        HAS_CURSES = False\n+except ImportError:\n     HAS_CURSES = False\n \n if HAS_CURSES:\n", "issue": "\"pause\" module fails with NameError when \"curses\" is not installed\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nTHIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.\r\nAlso test if the latest release, and devel branch are affected too.\r\nALWAYS add information AFTER (OUTSIDE) these html comments.\r\nOtherwise it may end up being automatically closed by our bot. -->\r\n\r\n##### SUMMARY\r\nThe \"pause\" module fails with a NameError when the curses Python module is not available.\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/usr/local/python/bin/ansible-playbook\", line 118, in <module>\r\n    exit_code = cli.run()\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/cli/playbook.py\", line 122, in run\r\n    results = pbex.run()\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/executor/playbook_executor.py\", line 159, in run\r\n    result = self._tqm.run(play=play)\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/executor/task_queue_manager.py\", line 289, in run\r\n    play_return = strategy.run(iterator, play_context)\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/plugins/strategy/linear.py\", line 247, in run\r\n    action = action_loader.get(task.action, class_only=True)\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/plugins/loader.py\", line 366, in get\r\n    self._module_cache[path] = self._load_module_source(name, path)\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/plugins/loader.py\", line 345, in _load_module_source\r\n    module = imp.load_source(full_name, path, module_file)\r\n  File \"/usr/local/python/lib/python2.7/site-packages/ansible/plugins/action/pause.py\", line 44, in <module>\r\n    except (ImportError, curses.error):\r\nNameError: name 'curses' is not defined\r\n```\r\n\r\nCaused by this code block introduced by https://github.com/ansible/ansible/pull/40134 to fix #35372:\r\n\r\n```python\r\ntry:\r\n    import curses\r\n    curses.setupterm()\r\n    HAS_CURSES = True\r\nexcept (ImportError, curses.error):\r\n    HAS_CURSES = False\r\n```\r\n\r\nA better fix would be to nest the curses.error try-catch within the block.\r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\nplugins/action/pause.py\r\n\r\n##### ANSIBLE VERSION\r\nansible 2.5.4\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = [u'/etc/ansible/modules']\r\n  ansible python module location = /usr/local/python/lib/python2.7/site-packages/ansible\r\n  executable location = /usr/local/python/bin/ansible\r\n  python version = 2.7.10 (default, Oct 11 2015, 23:13:31) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]\r\n\r\n##### CONFIGURATION\r\nN/A\r\n\r\n##### OS / ENVIRONMENT\r\nN/A\r\n\r\n##### STEPS TO REPRODUCE\r\nOn a machine without \"curses\", run:\r\n\r\n> $ ansible localhost -m pause -a 'seconds=1'\r\n> ERROR! Unexpected Exception, this is probably a bug: name 'curses' is not defined\r\n> to see the full traceback, use -vvv\r\n\r\n##### EXPECTED RESULTS\r\nN/A\r\n\r\n##### ACTUAL RESULTS\r\nSee above.\n", "before_files": [{"content": "# Copyright 2012, Tim Bielawa <tbielawa@redhat.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport datetime\nimport signal\nimport sys\nimport termios\nimport time\nimport tty\n\nfrom os import isatty\nfrom ansible.errors import AnsibleError\nfrom ansible.module_utils._text import to_text, to_native\nfrom ansible.module_utils.parsing.convert_bool import boolean\nfrom ansible.module_utils.six import PY3\nfrom ansible.plugins.action import ActionBase\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\ntry:\n    import curses\n    curses.setupterm()\n    HAS_CURSES = True\nexcept (ImportError, curses.error):\n    HAS_CURSES = False\n\nif HAS_CURSES:\n    MOVE_TO_BOL = curses.tigetstr('cr')\n    CLEAR_TO_EOL = curses.tigetstr('el')\nelse:\n    MOVE_TO_BOL = b'\\r'\n    CLEAR_TO_EOL = b'\\x1b[K'\n\n\nclass AnsibleTimeoutExceeded(Exception):\n    pass\n\n\ndef timeout_handler(signum, frame):\n    raise AnsibleTimeoutExceeded\n\n\ndef clear_line(stdout):\n    stdout.write(b'\\x1b[%s' % MOVE_TO_BOL)\n    stdout.write(b'\\x1b[%s' % CLEAR_TO_EOL)\n\n\nclass ActionModule(ActionBase):\n    ''' pauses execution for a length or time, or until input is received '''\n\n    PAUSE_TYPES = ['seconds', 'minutes', 'prompt', 'echo', '']\n    BYPASS_HOST_LOOP = True\n\n    def run(self, tmp=None, task_vars=None):\n        ''' run the pause action module '''\n        if task_vars is None:\n            task_vars = dict()\n\n        result = super(ActionModule, self).run(tmp, task_vars)\n        del tmp  # tmp no longer has any effect\n\n        duration_unit = 'minutes'\n        prompt = None\n        seconds = None\n        echo = True\n        echo_prompt = ''\n        result.update(dict(\n            changed=False,\n            rc=0,\n            stderr='',\n            stdout='',\n            start=None,\n            stop=None,\n            delta=None,\n            echo=echo\n        ))\n\n        if not set(self._task.args.keys()) <= set(self.PAUSE_TYPES):\n            result['failed'] = True\n            result['msg'] = \"Invalid argument given. Must be one of: %s\" % \", \".join(self.PAUSE_TYPES)\n            return result\n\n        # Should keystrokes be echoed to stdout?\n        if 'echo' in self._task.args:\n            try:\n                echo = boolean(self._task.args['echo'])\n            except TypeError as e:\n                result['failed'] = True\n                result['msg'] = to_native(e)\n                return result\n\n            # Add a note saying the output is hidden if echo is disabled\n            if not echo:\n                echo_prompt = ' (output is hidden)'\n\n        # Is 'prompt' a key in 'args'?\n        if 'prompt' in self._task.args:\n            prompt = \"[%s]\\n%s%s:\" % (self._task.get_name().strip(), self._task.args['prompt'], echo_prompt)\n        else:\n            # If no custom prompt is specified, set a default prompt\n            prompt = \"[%s]\\n%s%s:\" % (self._task.get_name().strip(), 'Press enter to continue, Ctrl+C to interrupt', echo_prompt)\n\n        # Are 'minutes' or 'seconds' keys that exist in 'args'?\n        if 'minutes' in self._task.args or 'seconds' in self._task.args:\n            try:\n                if 'minutes' in self._task.args:\n                    # The time() command operates in seconds so we need to\n                    # recalculate for minutes=X values.\n                    seconds = int(self._task.args['minutes']) * 60\n                else:\n                    seconds = int(self._task.args['seconds'])\n                    duration_unit = 'seconds'\n\n            except ValueError as e:\n                result['failed'] = True\n                result['msg'] = u\"non-integer value given for prompt duration:\\n%s\" % to_text(e)\n                return result\n\n        ########################################################################\n        # Begin the hard work!\n\n        start = time.time()\n        result['start'] = to_text(datetime.datetime.now())\n        result['user_input'] = b''\n\n        fd = None\n        old_settings = None\n        try:\n            if seconds is not None:\n                if seconds < 1:\n                    seconds = 1\n\n                # setup the alarm handler\n                signal.signal(signal.SIGALRM, timeout_handler)\n                signal.alarm(seconds)\n\n                # show the timer and control prompts\n                display.display(\"Pausing for %d seconds%s\" % (seconds, echo_prompt))\n                display.display(\"(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)\\r\"),\n\n                # show the prompt specified in the task\n                if 'prompt' in self._task.args:\n                    display.display(prompt)\n\n            else:\n                display.display(prompt)\n\n            # save the attributes on the existing (duped) stdin so\n            # that we can restore them later after we set raw mode\n            fd = None\n            try:\n                if PY3:\n                    stdin = self._connection._new_stdin.buffer\n                    stdout = sys.stdout.buffer\n                else:\n                    stdin = self._connection._new_stdin\n                    stdout = sys.stdout\n                fd = stdin.fileno()\n            except (ValueError, AttributeError):\n                # ValueError: someone is using a closed file descriptor as stdin\n                # AttributeError: someone is using a null file descriptor as stdin on windoez\n                stdin = None\n\n            if fd is not None:\n                if isatty(fd):\n\n                    # grab actual Ctrl+C sequence\n                    try:\n                        intr = termios.tcgetattr(fd)[6][termios.VINTR]\n                    except Exception:\n                        # unsupported/not present, use default\n                        intr = b'\\x03'  # value for Ctrl+C\n\n                    # get backspace sequences\n                    try:\n                        backspace = termios.tcgetattr(fd)[6][termios.VERASE]\n                    except Exception:\n                        backspace = [b'\\x7f', b'\\x08']\n\n                    old_settings = termios.tcgetattr(fd)\n                    tty.setraw(fd)\n                    tty.setraw(stdout.fileno())\n\n                    # Only echo input if no timeout is specified\n                    if not seconds and echo:\n                        new_settings = termios.tcgetattr(fd)\n                        new_settings[3] = new_settings[3] | termios.ECHO\n                        termios.tcsetattr(fd, termios.TCSANOW, new_settings)\n\n                    # flush the buffer to make sure no previous key presses\n                    # are read in below\n                    termios.tcflush(stdin, termios.TCIFLUSH)\n\n            while True:\n                try:\n                    if fd is not None:\n                        key_pressed = stdin.read(1)\n                        if key_pressed == intr:  # value for Ctrl+C\n                            clear_line(stdout)\n                            raise KeyboardInterrupt\n\n                    if not seconds:\n                        if fd is None or not isatty(fd):\n                            display.warning(\"Not waiting for response to prompt as stdin is not interactive\")\n                            break\n\n                        # read key presses and act accordingly\n                        if key_pressed in (b'\\r', b'\\n'):\n                            clear_line(stdout)\n                            break\n                        elif key_pressed in backspace:\n                            # delete a character if backspace is pressed\n                            result['user_input'] = result['user_input'][:-1]\n                            clear_line(stdout)\n                            if echo:\n                                stdout.write(result['user_input'])\n                            stdout.flush()\n                        else:\n                            result['user_input'] += key_pressed\n\n                except KeyboardInterrupt:\n                    signal.alarm(0)\n                    display.display(\"Press 'C' to continue the play or 'A' to abort \\r\"),\n                    if self._c_or_a(stdin):\n                        clear_line(stdout)\n                        break\n\n                    clear_line(stdout)\n\n                    raise AnsibleError('user requested abort!')\n\n        except AnsibleTimeoutExceeded:\n            # this is the exception we expect when the alarm signal\n            # fires, so we simply ignore it to move into the cleanup\n            pass\n        finally:\n            # cleanup and save some information\n            # restore the old settings for the duped stdin fd\n            if not(None in (fd, old_settings)) and isatty(fd):\n                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)\n\n            duration = time.time() - start\n            result['stop'] = to_text(datetime.datetime.now())\n            result['delta'] = int(duration)\n\n            if duration_unit == 'minutes':\n                duration = round(duration / 60.0, 2)\n            else:\n                duration = round(duration, 2)\n            result['stdout'] = \"Paused for %s %s\" % (duration, duration_unit)\n\n        result['user_input'] = to_text(result['user_input'], errors='surrogate_or_strict')\n        return result\n\n    def _c_or_a(self, stdin):\n        while True:\n            key_pressed = stdin.read(1)\n            if key_pressed.lower() == b'a':\n                return False\n            elif key_pressed.lower() == b'c':\n                return True\n", "path": "lib/ansible/plugins/action/pause.py"}], "after_files": [{"content": "# Copyright 2012, Tim Bielawa <tbielawa@redhat.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport datetime\nimport signal\nimport sys\nimport termios\nimport time\nimport tty\n\nfrom os import isatty\nfrom ansible.errors import AnsibleError\nfrom ansible.module_utils._text import to_text, to_native\nfrom ansible.module_utils.parsing.convert_bool import boolean\nfrom ansible.module_utils.six import PY3\nfrom ansible.plugins.action import ActionBase\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\ntry:\n    import curses\n\n    # Nest the try except since curses.error is not available if curses did not import\n    try:\n        curses.setupterm()\n        HAS_CURSES = True\n    except curses.error:\n        HAS_CURSES = False\nexcept ImportError:\n    HAS_CURSES = False\n\nif HAS_CURSES:\n    MOVE_TO_BOL = curses.tigetstr('cr')\n    CLEAR_TO_EOL = curses.tigetstr('el')\nelse:\n    MOVE_TO_BOL = b'\\r'\n    CLEAR_TO_EOL = b'\\x1b[K'\n\n\nclass AnsibleTimeoutExceeded(Exception):\n    pass\n\n\ndef timeout_handler(signum, frame):\n    raise AnsibleTimeoutExceeded\n\n\ndef clear_line(stdout):\n    stdout.write(b'\\x1b[%s' % MOVE_TO_BOL)\n    stdout.write(b'\\x1b[%s' % CLEAR_TO_EOL)\n\n\nclass ActionModule(ActionBase):\n    ''' pauses execution for a length or time, or until input is received '''\n\n    PAUSE_TYPES = ['seconds', 'minutes', 'prompt', 'echo', '']\n    BYPASS_HOST_LOOP = True\n\n    def run(self, tmp=None, task_vars=None):\n        ''' run the pause action module '''\n        if task_vars is None:\n            task_vars = dict()\n\n        result = super(ActionModule, self).run(tmp, task_vars)\n        del tmp  # tmp no longer has any effect\n\n        duration_unit = 'minutes'\n        prompt = None\n        seconds = None\n        echo = True\n        echo_prompt = ''\n        result.update(dict(\n            changed=False,\n            rc=0,\n            stderr='',\n            stdout='',\n            start=None,\n            stop=None,\n            delta=None,\n            echo=echo\n        ))\n\n        if not set(self._task.args.keys()) <= set(self.PAUSE_TYPES):\n            result['failed'] = True\n            result['msg'] = \"Invalid argument given. Must be one of: %s\" % \", \".join(self.PAUSE_TYPES)\n            return result\n\n        # Should keystrokes be echoed to stdout?\n        if 'echo' in self._task.args:\n            try:\n                echo = boolean(self._task.args['echo'])\n            except TypeError as e:\n                result['failed'] = True\n                result['msg'] = to_native(e)\n                return result\n\n            # Add a note saying the output is hidden if echo is disabled\n            if not echo:\n                echo_prompt = ' (output is hidden)'\n\n        # Is 'prompt' a key in 'args'?\n        if 'prompt' in self._task.args:\n            prompt = \"[%s]\\n%s%s:\" % (self._task.get_name().strip(), self._task.args['prompt'], echo_prompt)\n        else:\n            # If no custom prompt is specified, set a default prompt\n            prompt = \"[%s]\\n%s%s:\" % (self._task.get_name().strip(), 'Press enter to continue, Ctrl+C to interrupt', echo_prompt)\n\n        # Are 'minutes' or 'seconds' keys that exist in 'args'?\n        if 'minutes' in self._task.args or 'seconds' in self._task.args:\n            try:\n                if 'minutes' in self._task.args:\n                    # The time() command operates in seconds so we need to\n                    # recalculate for minutes=X values.\n                    seconds = int(self._task.args['minutes']) * 60\n                else:\n                    seconds = int(self._task.args['seconds'])\n                    duration_unit = 'seconds'\n\n            except ValueError as e:\n                result['failed'] = True\n                result['msg'] = u\"non-integer value given for prompt duration:\\n%s\" % to_text(e)\n                return result\n\n        ########################################################################\n        # Begin the hard work!\n\n        start = time.time()\n        result['start'] = to_text(datetime.datetime.now())\n        result['user_input'] = b''\n\n        fd = None\n        old_settings = None\n        try:\n            if seconds is not None:\n                if seconds < 1:\n                    seconds = 1\n\n                # setup the alarm handler\n                signal.signal(signal.SIGALRM, timeout_handler)\n                signal.alarm(seconds)\n\n                # show the timer and control prompts\n                display.display(\"Pausing for %d seconds%s\" % (seconds, echo_prompt))\n                display.display(\"(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)\\r\"),\n\n                # show the prompt specified in the task\n                if 'prompt' in self._task.args:\n                    display.display(prompt)\n\n            else:\n                display.display(prompt)\n\n            # save the attributes on the existing (duped) stdin so\n            # that we can restore them later after we set raw mode\n            fd = None\n            try:\n                if PY3:\n                    stdin = self._connection._new_stdin.buffer\n                    stdout = sys.stdout.buffer\n                else:\n                    stdin = self._connection._new_stdin\n                    stdout = sys.stdout\n                fd = stdin.fileno()\n            except (ValueError, AttributeError):\n                # ValueError: someone is using a closed file descriptor as stdin\n                # AttributeError: someone is using a null file descriptor as stdin on windoez\n                stdin = None\n\n            if fd is not None:\n                if isatty(fd):\n\n                    # grab actual Ctrl+C sequence\n                    try:\n                        intr = termios.tcgetattr(fd)[6][termios.VINTR]\n                    except Exception:\n                        # unsupported/not present, use default\n                        intr = b'\\x03'  # value for Ctrl+C\n\n                    # get backspace sequences\n                    try:\n                        backspace = termios.tcgetattr(fd)[6][termios.VERASE]\n                    except Exception:\n                        backspace = [b'\\x7f', b'\\x08']\n\n                    old_settings = termios.tcgetattr(fd)\n                    tty.setraw(fd)\n                    tty.setraw(stdout.fileno())\n\n                    # Only echo input if no timeout is specified\n                    if not seconds and echo:\n                        new_settings = termios.tcgetattr(fd)\n                        new_settings[3] = new_settings[3] | termios.ECHO\n                        termios.tcsetattr(fd, termios.TCSANOW, new_settings)\n\n                    # flush the buffer to make sure no previous key presses\n                    # are read in below\n                    termios.tcflush(stdin, termios.TCIFLUSH)\n\n            while True:\n                try:\n                    if fd is not None:\n                        key_pressed = stdin.read(1)\n                        if key_pressed == intr:  # value for Ctrl+C\n                            clear_line(stdout)\n                            raise KeyboardInterrupt\n\n                    if not seconds:\n                        if fd is None or not isatty(fd):\n                            display.warning(\"Not waiting for response to prompt as stdin is not interactive\")\n                            break\n\n                        # read key presses and act accordingly\n                        if key_pressed in (b'\\r', b'\\n'):\n                            clear_line(stdout)\n                            break\n                        elif key_pressed in backspace:\n                            # delete a character if backspace is pressed\n                            result['user_input'] = result['user_input'][:-1]\n                            clear_line(stdout)\n                            if echo:\n                                stdout.write(result['user_input'])\n                            stdout.flush()\n                        else:\n                            result['user_input'] += key_pressed\n\n                except KeyboardInterrupt:\n                    signal.alarm(0)\n                    display.display(\"Press 'C' to continue the play or 'A' to abort \\r\"),\n                    if self._c_or_a(stdin):\n                        clear_line(stdout)\n                        break\n\n                    clear_line(stdout)\n\n                    raise AnsibleError('user requested abort!')\n\n        except AnsibleTimeoutExceeded:\n            # this is the exception we expect when the alarm signal\n            # fires, so we simply ignore it to move into the cleanup\n            pass\n        finally:\n            # cleanup and save some information\n            # restore the old settings for the duped stdin fd\n            if not(None in (fd, old_settings)) and isatty(fd):\n                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)\n\n            duration = time.time() - start\n            result['stop'] = to_text(datetime.datetime.now())\n            result['delta'] = int(duration)\n\n            if duration_unit == 'minutes':\n                duration = round(duration / 60.0, 2)\n            else:\n                duration = round(duration, 2)\n            result['stdout'] = \"Paused for %s %s\" % (duration, duration_unit)\n\n        result['user_input'] = to_text(result['user_input'], errors='surrogate_or_strict')\n        return result\n\n    def _c_or_a(self, stdin):\n        while True:\n            key_pressed = stdin.read(1)\n            if key_pressed.lower() == b'a':\n                return False\n            elif key_pressed.lower() == b'c':\n                return True\n", "path": "lib/ansible/plugins/action/pause.py"}]}