We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SetConsoleMode throws an error when locust is run from Jenkins Powershell
<!-- 
If you have a general question about how to use Locust, please check Stack Overflow first https://stackoverflow.com/questions/tagged/locust

You can also ask new questions on SO, https://stackoverflow.com/questions/ask just remember to tag your question with "locust". Do not immediately post your issue here after posting to SO, wait for an answer there instead.

Use this form only for reporting actual bugs in locust. Be mindful that the developers of locust are unpaid volunteers, so make sure you have tried everything you can think of before filing a bug :) 
-->

### Describe the bug
<!-- A clear and concise description of what the bug is -->
Error message is printed out to console right after the test is started in Jenkins and job fails on exit code 2 even actual test passes. We have run locust performance tests over a year on Jenkins without similar problem. Tests started to fail recently after we updated Python and Locustio versions to latest. Locust test run without a problem if it is started on Windows desktop Powershell (or cmd).

### Expected behavior
<!-- Tell us what you think should happen -->
Locust tests should run on CI as they run on local machine.

### Actual behavior
<!-- Tell us what happens instead. Include screenshots if this an issue with the GUI. -->
Error "pywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')" is printed out to console right after the test is started in Jenkins and job fails on exit code 2 even actual test passes.

### Steps to reproduce
<!-- Please provide a minimal reproducible code example (https://stackoverflow.com/help/minimal-reproducible-example) --> 
```
from locust import User, task, between


class MyUser(User):
    @task
    def my_task(self):
        print("executing my_task")

    wait_time = between(0.5, 10)
```
Start command
`locust -f .\locust_test.py --headless --run-time 3`

Console from Jenkins:
```
[Locust_test] $ powershell.exe -NonInteractive -ExecutionPolicy Bypass -File C:\Users\locust\AppData\Local\Temp\jenkins11138277147510956709.ps1
[2020-12-10 19:13:09,846] robot-vm/INFO/locust.main: Run time limit set to 3 seconds
[2020-12-10 19:13:09,847] robot-vm/INFO/locust.main: Starting Locust 1.4.1
[2020-12-10 19:13:09,847] robot-vm/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)...
[2020-12-10 19:13:09,847] robot-vm/INFO/locust.runners: All users spawned: MyUser: 1 (1 total running)
Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 854, in gevent._gevent_cgreenlet.Greenlet.run
  File "c:\python39\lib\site-packages\locust\input_events.py", line 89, in input_listener_func
    poller = get_poller()
  File "c:\python39\lib\site-packages\locust\input_events.py", line 81, in get_poller
    return WindowsKeyPoller()
  File "c:\python39\lib\site-packages\locust\input_events.py", line 47, in __init__
    self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)
pywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')
2020-12-10T17:13:09Z <Greenlet at 0x19066ffdd00: input_listener_func> failed with error

 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

[2020-12-10 19:13:09,855] robot-vm/CRITICAL/locust.main: Unhandled exception in greenlet: <Greenlet at 0x19066ffdd00: input_listener_func>
Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 854, in gevent._gevent_cgreenlet.Greenlet.run
  File "c:\python39\lib\site-packages\locust\input_events.py", line 89, in input_listener_func
    poller = get_poller()
  File "c:\python39\lib\site-packages\locust\input_events.py", line 81, in get_poller
    return WindowsKeyPoller()
  File "c:\python39\lib\site-packages\locust\input_events.py", line 47, in __init__
    self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)
pywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')
 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

[2020-12-10 19:13:12,486] robot-vm/INFO/locust.main: Time limit reached. Stopping Locust.
[2020-12-10 19:13:12,486] robot-vm/INFO/locust.runners: Stopping 1 users
[2020-12-10 19:13:12,487] robot-vm/INFO/locust.runners: 1 Users have been stopped, 0 still running
[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Running teardowns...
[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Shutting down (exit code 2), bye.
[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Cleaning up runner...
 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

Response time percentiles (approximated)
 Type     Name                                                              50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|

executing my_task
Build step 'PowerShell' marked build as failure
Archiving artifacts
Finished: FAILURE

REST API
Jenkins 2.249.3
```
Console from local PC's Powershell
```
PS C:\workspace\tmp> locust -f .\locust_test.py --headless --run-time 3
[2020-12-10 18:55:07,816] WL313558/INFO/locust.main: Run time limit set to 3 seconds
[2020-12-10 18:55:07,816] WL313558/INFO/locust.main: Starting Locust 1.4.1
[2020-12-10 18:55:07,816] WL313558/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)...
[2020-12-10 18:55:07,816] WL313558/INFO/locust.runners: All users spawned: MyUser: 1 (1 total running)
 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

executing my_task
 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

executing my_task
[2020-12-10 18:55:10,512] WL313558/INFO/locust.main: Time limit reached. Stopping Locust.
[2020-12-10 18:55:10,513] WL313558/INFO/locust.runners: Stopping 1 users
[2020-12-10 18:55:10,513] WL313558/INFO/locust.runners: 1 Users have been stopped, 0 still running
[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Running teardowns...
[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Shutting down (exit code 0), bye.
[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Cleaning up runner...
 Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

Response time percentiles (approximated)
 Type     Name                                                              50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
```


### Environment

- OS: Windows 10
- Python version: 3.9.1
- Locust version: 1.4.1

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `locust/input_events.py`
Content:
```
1 import gevent
2 import logging
3 import os
4 
5 if os.name == "nt":
6     from win32api import STD_INPUT_HANDLE
7     from win32console import (
8         GetStdHandle,
9         KEY_EVENT,
10         ENABLE_ECHO_INPUT,
11         ENABLE_LINE_INPUT,
12         ENABLE_PROCESSED_INPUT,
13     )
14 else:
15     import sys
16     import select
17     import termios
18     import tty
19 
20 
21 class InitError(Exception):
22     pass
23 
24 
25 class UnixKeyPoller:
26     def __init__(self):
27         if sys.stdin.isatty():
28             self.stdin = sys.stdin.fileno()
29             self.tattr = termios.tcgetattr(self.stdin)
30             tty.setcbreak(self.stdin, termios.TCSANOW)
31         else:
32             raise InitError("Terminal was not a tty. Keyboard input disabled")
33 
34     def cleanup(self):
35         termios.tcsetattr(self.stdin, termios.TCSANOW, self.tattr)
36 
37     def poll(_self):
38         dr, dw, de = select.select([sys.stdin], [], [], 0)
39         if not dr == []:
40             return sys.stdin.read(1)
41         return None
42 
43 
44 class WindowsKeyPoller:
45     def __init__(self):
46         self.read_handle = GetStdHandle(STD_INPUT_HANDLE)
47         self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)
48         self.cur_event_length = 0
49         self.cur_keys_length = 0
50         self.captured_chars = []
51 
52     def cleanup(self):
53         pass
54 
55     def poll(self):
56         if self.captured_chars:
57             return self.captured_chars.pop(0)
58 
59         events_peek = self.read_handle.PeekConsoleInput(10000)
60 
61         if not events_peek:
62             return None
63 
64         if not len(events_peek) == self.cur_event_length:
65             for cur_event in events_peek[self.cur_event_length :]:
66                 if cur_event.EventType == KEY_EVENT:
67                     if ord(cur_event.Char) and cur_event.KeyDown:
68                         cur_char = str(cur_event.Char)
69                         self.captured_chars.append(cur_char)
70 
71             self.cur_event_length = len(events_peek)
72 
73         if self.captured_chars:
74             return self.captured_chars.pop(0)
75         else:
76             return None
77 
78 
79 def get_poller():
80     if os.name == "nt":
81         return WindowsKeyPoller()
82     else:
83         return UnixKeyPoller()
84 
85 
86 def input_listener(key_to_func_map):
87     def input_listener_func():
88         try:
89             poller = get_poller()
90         except InitError as e:
91             logging.info(e)
92             return
93 
94         try:
95             while True:
96                 input = poller.poll()
97                 if input:
98                     for key in key_to_func_map:
99                         if input == key:
100                             key_to_func_map[key]()
101                 else:
102                     gevent.sleep(0.2)
103         except Exception as e:
104             logging.warning(f"Exception in keyboard input poller: {e}")
105         finally:
106             poller.cleanup()
107 
108     return input_listener_func
109
```
--- 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/locust/input_events.py b/locust/input_events.py
--- a/locust/input_events.py
+++ b/locust/input_events.py
@@ -1,6 +1,7 @@
 import gevent
 import logging
 import os
+import sys
 
 if os.name == "nt":
     from win32api import STD_INPUT_HANDLE
@@ -12,7 +13,6 @@
         ENABLE_PROCESSED_INPUT,
     )
 else:
-    import sys
     import select
     import termios
     import tty
@@ -43,11 +43,14 @@
 
 class WindowsKeyPoller:
     def __init__(self):
-        self.read_handle = GetStdHandle(STD_INPUT_HANDLE)
-        self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)
-        self.cur_event_length = 0
-        self.cur_keys_length = 0
-        self.captured_chars = []
+        if sys.stdin.isatty():
+            self.read_handle = GetStdHandle(STD_INPUT_HANDLE)
+            self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)
+            self.cur_event_length = 0
+            self.cur_keys_length = 0
+            self.captured_chars = []
+        else:
+            raise InitError("Terminal was not a tty. Keyboard input disabled")
 
     def cleanup(self):
         pass

{"golden_diff": "diff --git a/locust/input_events.py b/locust/input_events.py\n--- a/locust/input_events.py\n+++ b/locust/input_events.py\n@@ -1,6 +1,7 @@\n import gevent\n import logging\n import os\n+import sys\n \n if os.name == \"nt\":\n     from win32api import STD_INPUT_HANDLE\n@@ -12,7 +13,6 @@\n         ENABLE_PROCESSED_INPUT,\n     )\n else:\n-    import sys\n     import select\n     import termios\n     import tty\n@@ -43,11 +43,14 @@\n \n class WindowsKeyPoller:\n     def __init__(self):\n-        self.read_handle = GetStdHandle(STD_INPUT_HANDLE)\n-        self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\n-        self.cur_event_length = 0\n-        self.cur_keys_length = 0\n-        self.captured_chars = []\n+        if sys.stdin.isatty():\n+            self.read_handle = GetStdHandle(STD_INPUT_HANDLE)\n+            self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\n+            self.cur_event_length = 0\n+            self.cur_keys_length = 0\n+            self.captured_chars = []\n+        else:\n+            raise InitError(\"Terminal was not a tty. Keyboard input disabled\")\n \n     def cleanup(self):\n         pass\n", "issue": "SetConsoleMode throws an error when locust is run from Jenkins Powershell\n<!-- \r\nIf you have a general question about how to use Locust, please check Stack Overflow first https://stackoverflow.com/questions/tagged/locust\r\n\r\nYou can also ask new questions on SO, https://stackoverflow.com/questions/ask just remember to tag your question with \"locust\". Do not immediately post your issue here after posting to SO, wait for an answer there instead.\r\n\r\nUse this form only for reporting actual bugs in locust. Be mindful that the developers of locust are unpaid volunteers, so make sure you have tried everything you can think of before filing a bug :) \r\n-->\r\n\r\n### Describe the bug\r\n<!-- A clear and concise description of what the bug is -->\r\nError message is printed out to console right after the test is started in Jenkins and job fails on exit code 2 even actual test passes. We have run locust performance tests over a year on Jenkins without similar problem. Tests started to fail recently after we updated Python and Locustio versions to latest. Locust test run without a problem if it is started on Windows desktop Powershell (or cmd).\r\n\r\n### Expected behavior\r\n<!-- Tell us what you think should happen -->\r\nLocust tests should run on CI as they run on local machine.\r\n\r\n### Actual behavior\r\n<!-- Tell us what happens instead. Include screenshots if this an issue with the GUI. -->\r\nError \"pywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')\" is printed out to console right after the test is started in Jenkins and job fails on exit code 2 even actual test passes.\r\n\r\n### Steps to reproduce\r\n<!-- Please provide a minimal reproducible code example (https://stackoverflow.com/help/minimal-reproducible-example) --> \r\n```\r\nfrom locust import User, task, between\r\n\r\n\r\nclass MyUser(User):\r\n    @task\r\n    def my_task(self):\r\n        print(\"executing my_task\")\r\n\r\n    wait_time = between(0.5, 10)\r\n```\r\nStart command\r\n`locust -f .\\locust_test.py --headless --run-time 3`\r\n\r\nConsole from Jenkins:\r\n```\r\n[Locust_test] $ powershell.exe -NonInteractive -ExecutionPolicy Bypass -File C:\\Users\\locust\\AppData\\Local\\Temp\\jenkins11138277147510956709.ps1\r\n[2020-12-10 19:13:09,846] robot-vm/INFO/locust.main: Run time limit set to 3 seconds\r\n[2020-12-10 19:13:09,847] robot-vm/INFO/locust.main: Starting Locust 1.4.1\r\n[2020-12-10 19:13:09,847] robot-vm/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)...\r\n[2020-12-10 19:13:09,847] robot-vm/INFO/locust.runners: All users spawned: MyUser: 1 (1 total running)\r\nTraceback (most recent call last):\r\n  File \"src/gevent/greenlet.py\", line 854, in gevent._gevent_cgreenlet.Greenlet.run\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 89, in input_listener_func\r\n    poller = get_poller()\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 81, in get_poller\r\n    return WindowsKeyPoller()\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 47, in __init__\r\n    self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\r\npywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')\r\n2020-12-10T17:13:09Z <Greenlet at 0x19066ffdd00: input_listener_func> failed with error\r\n\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\n[2020-12-10 19:13:09,855] robot-vm/CRITICAL/locust.main: Unhandled exception in greenlet: <Greenlet at 0x19066ffdd00: input_listener_func>\r\nTraceback (most recent call last):\r\n  File \"src/gevent/greenlet.py\", line 854, in gevent._gevent_cgreenlet.Greenlet.run\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 89, in input_listener_func\r\n    poller = get_poller()\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 81, in get_poller\r\n    return WindowsKeyPoller()\r\n  File \"c:\\python39\\lib\\site-packages\\locust\\input_events.py\", line 47, in __init__\r\n    self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\r\npywintypes.error: (6, 'SetConsoleMode', 'The handle is invalid.')\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\n[2020-12-10 19:13:12,486] robot-vm/INFO/locust.main: Time limit reached. Stopping Locust.\r\n[2020-12-10 19:13:12,486] robot-vm/INFO/locust.runners: Stopping 1 users\r\n[2020-12-10 19:13:12,487] robot-vm/INFO/locust.runners: 1 Users have been stopped, 0 still running\r\n[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Running teardowns...\r\n[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Shutting down (exit code 2), bye.\r\n[2020-12-10 19:13:12,487] robot-vm/INFO/locust.main: Cleaning up runner...\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\nResponse time percentiles (approximated)\r\n Type     Name                                                              50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs\r\n--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|\r\n--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|\r\n\r\nexecuting my_task\r\nBuild step 'PowerShell' marked build as failure\r\nArchiving artifacts\r\nFinished: FAILURE\r\n\r\nREST API\r\nJenkins 2.249.3\r\n```\r\nConsole from local PC's Powershell\r\n```\r\nPS C:\\workspace\\tmp> locust -f .\\locust_test.py --headless --run-time 3\r\n[2020-12-10 18:55:07,816] WL313558/INFO/locust.main: Run time limit set to 3 seconds\r\n[2020-12-10 18:55:07,816] WL313558/INFO/locust.main: Starting Locust 1.4.1\r\n[2020-12-10 18:55:07,816] WL313558/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)...\r\n[2020-12-10 18:55:07,816] WL313558/INFO/locust.runners: All users spawned: MyUser: 1 (1 total running)\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\nexecuting my_task\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\nexecuting my_task\r\n[2020-12-10 18:55:10,512] WL313558/INFO/locust.main: Time limit reached. Stopping Locust.\r\n[2020-12-10 18:55:10,513] WL313558/INFO/locust.runners: Stopping 1 users\r\n[2020-12-10 18:55:10,513] WL313558/INFO/locust.runners: 1 Users have been stopped, 0 still running\r\n[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Running teardowns...\r\n[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Shutting down (exit code 0), bye.\r\n[2020-12-10 18:55:10,513] WL313558/INFO/locust.main: Cleaning up runner...\r\n Name                                                          # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n--------------------------------------------------------------------------------------------------------------------------------------------\r\n Aggregated                                                         0     0(0.00%)  |       0       0       0       0  |    0.00    0.00\r\n\r\nResponse time percentiles (approximated)\r\n Type     Name                                                              50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs\r\n--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|\r\n--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|\r\n```\r\n\r\n\r\n### Environment\r\n\r\n- OS: Windows 10\r\n- Python version: 3.9.1\r\n- Locust version: 1.4.1\n", "before_files": [{"content": "import gevent\nimport logging\nimport os\n\nif os.name == \"nt\":\n    from win32api import STD_INPUT_HANDLE\n    from win32console import (\n        GetStdHandle,\n        KEY_EVENT,\n        ENABLE_ECHO_INPUT,\n        ENABLE_LINE_INPUT,\n        ENABLE_PROCESSED_INPUT,\n    )\nelse:\n    import sys\n    import select\n    import termios\n    import tty\n\n\nclass InitError(Exception):\n    pass\n\n\nclass UnixKeyPoller:\n    def __init__(self):\n        if sys.stdin.isatty():\n            self.stdin = sys.stdin.fileno()\n            self.tattr = termios.tcgetattr(self.stdin)\n            tty.setcbreak(self.stdin, termios.TCSANOW)\n        else:\n            raise InitError(\"Terminal was not a tty. Keyboard input disabled\")\n\n    def cleanup(self):\n        termios.tcsetattr(self.stdin, termios.TCSANOW, self.tattr)\n\n    def poll(_self):\n        dr, dw, de = select.select([sys.stdin], [], [], 0)\n        if not dr == []:\n            return sys.stdin.read(1)\n        return None\n\n\nclass WindowsKeyPoller:\n    def __init__(self):\n        self.read_handle = GetStdHandle(STD_INPUT_HANDLE)\n        self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\n        self.cur_event_length = 0\n        self.cur_keys_length = 0\n        self.captured_chars = []\n\n    def cleanup(self):\n        pass\n\n    def poll(self):\n        if self.captured_chars:\n            return self.captured_chars.pop(0)\n\n        events_peek = self.read_handle.PeekConsoleInput(10000)\n\n        if not events_peek:\n            return None\n\n        if not len(events_peek) == self.cur_event_length:\n            for cur_event in events_peek[self.cur_event_length :]:\n                if cur_event.EventType == KEY_EVENT:\n                    if ord(cur_event.Char) and cur_event.KeyDown:\n                        cur_char = str(cur_event.Char)\n                        self.captured_chars.append(cur_char)\n\n            self.cur_event_length = len(events_peek)\n\n        if self.captured_chars:\n            return self.captured_chars.pop(0)\n        else:\n            return None\n\n\ndef get_poller():\n    if os.name == \"nt\":\n        return WindowsKeyPoller()\n    else:\n        return UnixKeyPoller()\n\n\ndef input_listener(key_to_func_map):\n    def input_listener_func():\n        try:\n            poller = get_poller()\n        except InitError as e:\n            logging.info(e)\n            return\n\n        try:\n            while True:\n                input = poller.poll()\n                if input:\n                    for key in key_to_func_map:\n                        if input == key:\n                            key_to_func_map[key]()\n                else:\n                    gevent.sleep(0.2)\n        except Exception as e:\n            logging.warning(f\"Exception in keyboard input poller: {e}\")\n        finally:\n            poller.cleanup()\n\n    return input_listener_func\n", "path": "locust/input_events.py"}], "after_files": [{"content": "import gevent\nimport logging\nimport os\nimport sys\n\nif os.name == \"nt\":\n    from win32api import STD_INPUT_HANDLE\n    from win32console import (\n        GetStdHandle,\n        KEY_EVENT,\n        ENABLE_ECHO_INPUT,\n        ENABLE_LINE_INPUT,\n        ENABLE_PROCESSED_INPUT,\n    )\nelse:\n    import select\n    import termios\n    import tty\n\n\nclass InitError(Exception):\n    pass\n\n\nclass UnixKeyPoller:\n    def __init__(self):\n        if sys.stdin.isatty():\n            self.stdin = sys.stdin.fileno()\n            self.tattr = termios.tcgetattr(self.stdin)\n            tty.setcbreak(self.stdin, termios.TCSANOW)\n        else:\n            raise InitError(\"Terminal was not a tty. Keyboard input disabled\")\n\n    def cleanup(self):\n        termios.tcsetattr(self.stdin, termios.TCSANOW, self.tattr)\n\n    def poll(_self):\n        dr, dw, de = select.select([sys.stdin], [], [], 0)\n        if not dr == []:\n            return sys.stdin.read(1)\n        return None\n\n\nclass WindowsKeyPoller:\n    def __init__(self):\n        if sys.stdin.isatty():\n            self.read_handle = GetStdHandle(STD_INPUT_HANDLE)\n            self.read_handle.SetConsoleMode(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT)\n            self.cur_event_length = 0\n            self.cur_keys_length = 0\n            self.captured_chars = []\n        else:\n            raise InitError(\"Terminal was not a tty. Keyboard input disabled\")\n\n    def cleanup(self):\n        pass\n\n    def poll(self):\n        if self.captured_chars:\n            return self.captured_chars.pop(0)\n\n        events_peek = self.read_handle.PeekConsoleInput(10000)\n\n        if not events_peek:\n            return None\n\n        if not len(events_peek) == self.cur_event_length:\n            for cur_event in events_peek[self.cur_event_length :]:\n                if cur_event.EventType == KEY_EVENT:\n                    if ord(cur_event.Char) and cur_event.KeyDown:\n                        cur_char = str(cur_event.Char)\n                        self.captured_chars.append(cur_char)\n\n            self.cur_event_length = len(events_peek)\n\n        if self.captured_chars:\n            return self.captured_chars.pop(0)\n        else:\n            return None\n\n\ndef get_poller():\n    if os.name == \"nt\":\n        return WindowsKeyPoller()\n    else:\n        return UnixKeyPoller()\n\n\ndef input_listener(key_to_func_map):\n    def input_listener_func():\n        try:\n            poller = get_poller()\n        except InitError as e:\n            logging.info(e)\n            return\n\n        try:\n            while True:\n                input = poller.poll()\n                if input:\n                    for key in key_to_func_map:\n                        if input == key:\n                            key_to_func_map[key]()\n                else:\n                    gevent.sleep(0.2)\n        except Exception as e:\n            logging.warning(f\"Exception in keyboard input poller: {e}\")\n        finally:\n            poller.cleanup()\n\n    return input_listener_func\n", "path": "locust/input_events.py"}]}