We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Reloader using wrong python executable
Python version: `3.6.5`
Werkzeug version: `0.15.4`

The change made as a result of this PR, https://github.com/pallets/werkzeug/pull/1242, causes inconsistent behaviour when specifying how a script should be run.

`sys.executable` might not (validly) equal the file's shebang.
For example, a developer might want to test their program with a different version of python, or run under a different environment to where the program would be deployed.

The biggest use case is running a script with a debugger, as seen with this issue: https://github.com/pallets/werkzeug/issues/1493

The developer shouldn't be forced to change shebangs or to change the execute permissions of the file to make it work.

In summary, if an executable is specified, then that executable should be used; regardless of the file's shebang.

Since the intention was for this to work with NixOS, I propose that if the functionality is to remain then it should be exclusive to NixOS (where it was pointed out on the PR that it seems to be a problem with the OS), similarly to Windows. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/werkzeug/_reloader.py`
Content:
```
1 import os
2 import subprocess
3 import sys
4 import threading
5 import time
6 from itertools import chain
7 
8 from ._compat import iteritems
9 from ._compat import PY2
10 from ._compat import text_type
11 from ._internal import _log
12 
13 
14 def _iter_module_files():
15     """This iterates over all relevant Python files.  It goes through all
16     loaded files from modules, all files in folders of already loaded modules
17     as well as all files reachable through a package.
18     """
19     # The list call is necessary on Python 3 in case the module
20     # dictionary modifies during iteration.
21     for module in list(sys.modules.values()):
22         if module is None:
23             continue
24         filename = getattr(module, "__file__", None)
25         if filename:
26             if os.path.isdir(filename) and os.path.exists(
27                 os.path.join(filename, "__init__.py")
28             ):
29                 filename = os.path.join(filename, "__init__.py")
30 
31             old = None
32             while not os.path.isfile(filename):
33                 old = filename
34                 filename = os.path.dirname(filename)
35                 if filename == old:
36                     break
37             else:
38                 if filename[-4:] in (".pyc", ".pyo"):
39                     filename = filename[:-1]
40                 yield filename
41 
42 
43 def _find_observable_paths(extra_files=None):
44     """Finds all paths that should be observed."""
45     rv = set(
46         os.path.dirname(os.path.abspath(x)) if os.path.isfile(x) else os.path.abspath(x)
47         for x in sys.path
48     )
49 
50     for filename in extra_files or ():
51         rv.add(os.path.dirname(os.path.abspath(filename)))
52 
53     for module in list(sys.modules.values()):
54         fn = getattr(module, "__file__", None)
55         if fn is None:
56             continue
57         fn = os.path.abspath(fn)
58         rv.add(os.path.dirname(fn))
59 
60     return _find_common_roots(rv)
61 
62 
63 def _get_args_for_reloading():
64     """Returns the executable. This contains a workaround for windows
65     if the executable is incorrectly reported to not have the .exe
66     extension which can cause bugs on reloading.  This also contains
67     a workaround for linux where the file is executable (possibly with
68     a program other than python)
69     """
70     rv = [sys.executable]
71     py_script = sys.argv[0]
72     args = sys.argv[1:]
73     # Need to look at main module to determine how it was executed.
74     __main__ = sys.modules["__main__"]
75 
76     # The value of __package__ indicates how Python was called. It may
77     # not exist if a setuptools script is installed as an egg.
78     if getattr(__main__, "__package__", None) is None:
79         # Executed a file, like "python app.py".
80         py_script = os.path.abspath(py_script)
81 
82         if os.name == "nt":
83             # Windows entry points have ".exe" extension and should be
84             # called directly.
85             if not os.path.exists(py_script) and os.path.exists(py_script + ".exe"):
86                 py_script += ".exe"
87 
88             if (
89                 os.path.splitext(rv[0])[1] == ".exe"
90                 and os.path.splitext(py_script)[1] == ".exe"
91             ):
92                 rv.pop(0)
93 
94         elif os.path.isfile(py_script) and os.access(py_script, os.X_OK):
95             # The file is marked as executable. Nix adds a wrapper that
96             # shouldn't be called with the Python executable.
97             rv.pop(0)
98 
99         rv.append(py_script)
100     else:
101         # Executed a module, like "python -m werkzeug.serving".
102         if sys.argv[0] == "-m":
103             # Flask works around previous behavior by putting
104             # "-m flask" in sys.argv.
105             # TODO remove this once Flask no longer misbehaves
106             args = sys.argv
107         else:
108             if os.path.isfile(py_script):
109                 # Rewritten by Python from "-m script" to "/path/to/script.py".
110                 py_module = __main__.__package__
111                 name = os.path.splitext(os.path.basename(py_script))[0]
112 
113                 if name != "__main__":
114                     py_module += "." + name
115             else:
116                 # Incorrectly rewritten by pydevd debugger from "-m script" to "script".
117                 py_module = py_script
118 
119             rv.extend(("-m", py_module.lstrip(".")))
120 
121     rv.extend(args)
122     return rv
123 
124 
125 def _find_common_roots(paths):
126     """Out of some paths it finds the common roots that need monitoring."""
127     paths = [x.split(os.path.sep) for x in paths]
128     root = {}
129     for chunks in sorted(paths, key=len, reverse=True):
130         node = root
131         for chunk in chunks:
132             node = node.setdefault(chunk, {})
133         node.clear()
134 
135     rv = set()
136 
137     def _walk(node, path):
138         for prefix, child in iteritems(node):
139             _walk(child, path + (prefix,))
140         if not node:
141             rv.add("/".join(path))
142 
143     _walk(root, ())
144     return rv
145 
146 
147 class ReloaderLoop(object):
148     name = None
149 
150     # monkeypatched by testsuite. wrapping with `staticmethod` is required in
151     # case time.sleep has been replaced by a non-c function (e.g. by
152     # `eventlet.monkey_patch`) before we get here
153     _sleep = staticmethod(time.sleep)
154 
155     def __init__(self, extra_files=None, interval=1):
156         self.extra_files = set(os.path.abspath(x) for x in extra_files or ())
157         self.interval = interval
158 
159     def run(self):
160         pass
161 
162     def restart_with_reloader(self):
163         """Spawn a new Python interpreter with the same arguments as this one,
164         but running the reloader thread.
165         """
166         while 1:
167             _log("info", " * Restarting with %s" % self.name)
168             args = _get_args_for_reloading()
169 
170             # a weird bug on windows. sometimes unicode strings end up in the
171             # environment and subprocess.call does not like this, encode them
172             # to latin1 and continue.
173             if os.name == "nt" and PY2:
174                 new_environ = {}
175                 for key, value in iteritems(os.environ):
176                     if isinstance(key, text_type):
177                         key = key.encode("iso-8859-1")
178                     if isinstance(value, text_type):
179                         value = value.encode("iso-8859-1")
180                     new_environ[key] = value
181             else:
182                 new_environ = os.environ.copy()
183 
184             new_environ["WERKZEUG_RUN_MAIN"] = "true"
185             exit_code = subprocess.call(args, env=new_environ, close_fds=False)
186             if exit_code != 3:
187                 return exit_code
188 
189     def trigger_reload(self, filename):
190         self.log_reload(filename)
191         sys.exit(3)
192 
193     def log_reload(self, filename):
194         filename = os.path.abspath(filename)
195         _log("info", " * Detected change in %r, reloading" % filename)
196 
197 
198 class StatReloaderLoop(ReloaderLoop):
199     name = "stat"
200 
201     def run(self):
202         mtimes = {}
203         while 1:
204             for filename in chain(_iter_module_files(), self.extra_files):
205                 try:
206                     mtime = os.stat(filename).st_mtime
207                 except OSError:
208                     continue
209 
210                 old_time = mtimes.get(filename)
211                 if old_time is None:
212                     mtimes[filename] = mtime
213                     continue
214                 elif mtime > old_time:
215                     self.trigger_reload(filename)
216             self._sleep(self.interval)
217 
218 
219 class WatchdogReloaderLoop(ReloaderLoop):
220     def __init__(self, *args, **kwargs):
221         ReloaderLoop.__init__(self, *args, **kwargs)
222         from watchdog.observers import Observer
223         from watchdog.events import FileSystemEventHandler
224 
225         self.observable_paths = set()
226 
227         def _check_modification(filename):
228             if filename in self.extra_files:
229                 self.trigger_reload(filename)
230             dirname = os.path.dirname(filename)
231             if dirname.startswith(tuple(self.observable_paths)):
232                 if filename.endswith((".pyc", ".pyo", ".py")):
233                     self.trigger_reload(filename)
234 
235         class _CustomHandler(FileSystemEventHandler):
236             def on_created(self, event):
237                 _check_modification(event.src_path)
238 
239             def on_modified(self, event):
240                 _check_modification(event.src_path)
241 
242             def on_moved(self, event):
243                 _check_modification(event.src_path)
244                 _check_modification(event.dest_path)
245 
246             def on_deleted(self, event):
247                 _check_modification(event.src_path)
248 
249         reloader_name = Observer.__name__.lower()
250         if reloader_name.endswith("observer"):
251             reloader_name = reloader_name[:-8]
252         reloader_name += " reloader"
253 
254         self.name = reloader_name
255 
256         self.observer_class = Observer
257         self.event_handler = _CustomHandler()
258         self.should_reload = False
259 
260     def trigger_reload(self, filename):
261         # This is called inside an event handler, which means throwing
262         # SystemExit has no effect.
263         # https://github.com/gorakhargosh/watchdog/issues/294
264         self.should_reload = True
265         self.log_reload(filename)
266 
267     def run(self):
268         watches = {}
269         observer = self.observer_class()
270         observer.start()
271 
272         try:
273             while not self.should_reload:
274                 to_delete = set(watches)
275                 paths = _find_observable_paths(self.extra_files)
276                 for path in paths:
277                     if path not in watches:
278                         try:
279                             watches[path] = observer.schedule(
280                                 self.event_handler, path, recursive=True
281                             )
282                         except OSError:
283                             # Clear this path from list of watches We don't want
284                             # the same error message showing again in the next
285                             # iteration.
286                             watches[path] = None
287                     to_delete.discard(path)
288                 for path in to_delete:
289                     watch = watches.pop(path, None)
290                     if watch is not None:
291                         observer.unschedule(watch)
292                 self.observable_paths = paths
293                 self._sleep(self.interval)
294         finally:
295             observer.stop()
296             observer.join()
297 
298         sys.exit(3)
299 
300 
301 reloader_loops = {"stat": StatReloaderLoop, "watchdog": WatchdogReloaderLoop}
302 
303 try:
304     __import__("watchdog.observers")
305 except ImportError:
306     reloader_loops["auto"] = reloader_loops["stat"]
307 else:
308     reloader_loops["auto"] = reloader_loops["watchdog"]
309 
310 
311 def ensure_echo_on():
312     """Ensure that echo mode is enabled. Some tools such as PDB disable
313     it which causes usability issues after reload."""
314     # tcgetattr will fail if stdin isn't a tty
315     if not sys.stdin.isatty():
316         return
317     try:
318         import termios
319     except ImportError:
320         return
321     attributes = termios.tcgetattr(sys.stdin)
322     if not attributes[3] & termios.ECHO:
323         attributes[3] |= termios.ECHO
324         termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes)
325 
326 
327 def run_with_reloader(main_func, extra_files=None, interval=1, reloader_type="auto"):
328     """Run the given function in an independent python interpreter."""
329     import signal
330 
331     reloader = reloader_loops[reloader_type](extra_files, interval)
332     signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
333     try:
334         if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
335             ensure_echo_on()
336             t = threading.Thread(target=main_func, args=())
337             t.setDaemon(True)
338             t.start()
339             reloader.run()
340         else:
341             sys.exit(reloader.restart_with_reloader())
342     except KeyboardInterrupt:
343         pass
344
```
--- 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/src/werkzeug/_reloader.py b/src/werkzeug/_reloader.py
--- a/src/werkzeug/_reloader.py
+++ b/src/werkzeug/_reloader.py
@@ -61,11 +61,8 @@
 
 
 def _get_args_for_reloading():
-    """Returns the executable. This contains a workaround for windows
-    if the executable is incorrectly reported to not have the .exe
-    extension which can cause bugs on reloading.  This also contains
-    a workaround for linux where the file is executable (possibly with
-    a program other than python)
+    """Determine how the script was executed, and return the args needed
+    to execute it again in a new process.
     """
     rv = [sys.executable]
     py_script = sys.argv[0]
@@ -91,11 +88,6 @@
             ):
                 rv.pop(0)
 
-        elif os.path.isfile(py_script) and os.access(py_script, os.X_OK):
-            # The file is marked as executable. Nix adds a wrapper that
-            # shouldn't be called with the Python executable.
-            rv.pop(0)
-
         rv.append(py_script)
     else:
         # Executed a module, like "python -m werkzeug.serving".

{"golden_diff": "diff --git a/src/werkzeug/_reloader.py b/src/werkzeug/_reloader.py\n--- a/src/werkzeug/_reloader.py\n+++ b/src/werkzeug/_reloader.py\n@@ -61,11 +61,8 @@\n \n \n def _get_args_for_reloading():\n-    \"\"\"Returns the executable. This contains a workaround for windows\n-    if the executable is incorrectly reported to not have the .exe\n-    extension which can cause bugs on reloading.  This also contains\n-    a workaround for linux where the file is executable (possibly with\n-    a program other than python)\n+    \"\"\"Determine how the script was executed, and return the args needed\n+    to execute it again in a new process.\n     \"\"\"\n     rv = [sys.executable]\n     py_script = sys.argv[0]\n@@ -91,11 +88,6 @@\n             ):\n                 rv.pop(0)\n \n-        elif os.path.isfile(py_script) and os.access(py_script, os.X_OK):\n-            # The file is marked as executable. Nix adds a wrapper that\n-            # shouldn't be called with the Python executable.\n-            rv.pop(0)\n-\n         rv.append(py_script)\n     else:\n         # Executed a module, like \"python -m werkzeug.serving\".\n", "issue": "Reloader using wrong python executable\nPython version: `3.6.5`\r\nWerkzeug version: `0.15.4`\r\n\r\nThe change made as a result of this PR, https://github.com/pallets/werkzeug/pull/1242, causes inconsistent behaviour when specifying how a script should be run.\r\n\r\n`sys.executable` might not (validly) equal the file's shebang.\r\nFor example, a developer might want to test their program with a different version of python, or run under a different environment to where the program would be deployed.\r\n\r\nThe biggest use case is running a script with a debugger, as seen with this issue: https://github.com/pallets/werkzeug/issues/1493\r\n\r\nThe developer shouldn't be forced to change shebangs or to change the execute permissions of the file to make it work.\r\n\r\nIn summary, if an executable is specified, then that executable should be used; regardless of the file's shebang.\r\n\r\nSince the intention was for this to work with NixOS, I propose that if the functionality is to remain then it should be exclusive to NixOS (where it was pointed out on the PR that it seems to be a problem with the OS), similarly to Windows. \n", "before_files": [{"content": "import os\nimport subprocess\nimport sys\nimport threading\nimport time\nfrom itertools import chain\n\nfrom ._compat import iteritems\nfrom ._compat import PY2\nfrom ._compat import text_type\nfrom ._internal import _log\n\n\ndef _iter_module_files():\n    \"\"\"This iterates over all relevant Python files.  It goes through all\n    loaded files from modules, all files in folders of already loaded modules\n    as well as all files reachable through a package.\n    \"\"\"\n    # The list call is necessary on Python 3 in case the module\n    # dictionary modifies during iteration.\n    for module in list(sys.modules.values()):\n        if module is None:\n            continue\n        filename = getattr(module, \"__file__\", None)\n        if filename:\n            if os.path.isdir(filename) and os.path.exists(\n                os.path.join(filename, \"__init__.py\")\n            ):\n                filename = os.path.join(filename, \"__init__.py\")\n\n            old = None\n            while not os.path.isfile(filename):\n                old = filename\n                filename = os.path.dirname(filename)\n                if filename == old:\n                    break\n            else:\n                if filename[-4:] in (\".pyc\", \".pyo\"):\n                    filename = filename[:-1]\n                yield filename\n\n\ndef _find_observable_paths(extra_files=None):\n    \"\"\"Finds all paths that should be observed.\"\"\"\n    rv = set(\n        os.path.dirname(os.path.abspath(x)) if os.path.isfile(x) else os.path.abspath(x)\n        for x in sys.path\n    )\n\n    for filename in extra_files or ():\n        rv.add(os.path.dirname(os.path.abspath(filename)))\n\n    for module in list(sys.modules.values()):\n        fn = getattr(module, \"__file__\", None)\n        if fn is None:\n            continue\n        fn = os.path.abspath(fn)\n        rv.add(os.path.dirname(fn))\n\n    return _find_common_roots(rv)\n\n\ndef _get_args_for_reloading():\n    \"\"\"Returns the executable. This contains a workaround for windows\n    if the executable is incorrectly reported to not have the .exe\n    extension which can cause bugs on reloading.  This also contains\n    a workaround for linux where the file is executable (possibly with\n    a program other than python)\n    \"\"\"\n    rv = [sys.executable]\n    py_script = sys.argv[0]\n    args = sys.argv[1:]\n    # Need to look at main module to determine how it was executed.\n    __main__ = sys.modules[\"__main__\"]\n\n    # The value of __package__ indicates how Python was called. It may\n    # not exist if a setuptools script is installed as an egg.\n    if getattr(__main__, \"__package__\", None) is None:\n        # Executed a file, like \"python app.py\".\n        py_script = os.path.abspath(py_script)\n\n        if os.name == \"nt\":\n            # Windows entry points have \".exe\" extension and should be\n            # called directly.\n            if not os.path.exists(py_script) and os.path.exists(py_script + \".exe\"):\n                py_script += \".exe\"\n\n            if (\n                os.path.splitext(rv[0])[1] == \".exe\"\n                and os.path.splitext(py_script)[1] == \".exe\"\n            ):\n                rv.pop(0)\n\n        elif os.path.isfile(py_script) and os.access(py_script, os.X_OK):\n            # The file is marked as executable. Nix adds a wrapper that\n            # shouldn't be called with the Python executable.\n            rv.pop(0)\n\n        rv.append(py_script)\n    else:\n        # Executed a module, like \"python -m werkzeug.serving\".\n        if sys.argv[0] == \"-m\":\n            # Flask works around previous behavior by putting\n            # \"-m flask\" in sys.argv.\n            # TODO remove this once Flask no longer misbehaves\n            args = sys.argv\n        else:\n            if os.path.isfile(py_script):\n                # Rewritten by Python from \"-m script\" to \"/path/to/script.py\".\n                py_module = __main__.__package__\n                name = os.path.splitext(os.path.basename(py_script))[0]\n\n                if name != \"__main__\":\n                    py_module += \".\" + name\n            else:\n                # Incorrectly rewritten by pydevd debugger from \"-m script\" to \"script\".\n                py_module = py_script\n\n            rv.extend((\"-m\", py_module.lstrip(\".\")))\n\n    rv.extend(args)\n    return rv\n\n\ndef _find_common_roots(paths):\n    \"\"\"Out of some paths it finds the common roots that need monitoring.\"\"\"\n    paths = [x.split(os.path.sep) for x in paths]\n    root = {}\n    for chunks in sorted(paths, key=len, reverse=True):\n        node = root\n        for chunk in chunks:\n            node = node.setdefault(chunk, {})\n        node.clear()\n\n    rv = set()\n\n    def _walk(node, path):\n        for prefix, child in iteritems(node):\n            _walk(child, path + (prefix,))\n        if not node:\n            rv.add(\"/\".join(path))\n\n    _walk(root, ())\n    return rv\n\n\nclass ReloaderLoop(object):\n    name = None\n\n    # monkeypatched by testsuite. wrapping with `staticmethod` is required in\n    # case time.sleep has been replaced by a non-c function (e.g. by\n    # `eventlet.monkey_patch`) before we get here\n    _sleep = staticmethod(time.sleep)\n\n    def __init__(self, extra_files=None, interval=1):\n        self.extra_files = set(os.path.abspath(x) for x in extra_files or ())\n        self.interval = interval\n\n    def run(self):\n        pass\n\n    def restart_with_reloader(self):\n        \"\"\"Spawn a new Python interpreter with the same arguments as this one,\n        but running the reloader thread.\n        \"\"\"\n        while 1:\n            _log(\"info\", \" * Restarting with %s\" % self.name)\n            args = _get_args_for_reloading()\n\n            # a weird bug on windows. sometimes unicode strings end up in the\n            # environment and subprocess.call does not like this, encode them\n            # to latin1 and continue.\n            if os.name == \"nt\" and PY2:\n                new_environ = {}\n                for key, value in iteritems(os.environ):\n                    if isinstance(key, text_type):\n                        key = key.encode(\"iso-8859-1\")\n                    if isinstance(value, text_type):\n                        value = value.encode(\"iso-8859-1\")\n                    new_environ[key] = value\n            else:\n                new_environ = os.environ.copy()\n\n            new_environ[\"WERKZEUG_RUN_MAIN\"] = \"true\"\n            exit_code = subprocess.call(args, env=new_environ, close_fds=False)\n            if exit_code != 3:\n                return exit_code\n\n    def trigger_reload(self, filename):\n        self.log_reload(filename)\n        sys.exit(3)\n\n    def log_reload(self, filename):\n        filename = os.path.abspath(filename)\n        _log(\"info\", \" * Detected change in %r, reloading\" % filename)\n\n\nclass StatReloaderLoop(ReloaderLoop):\n    name = \"stat\"\n\n    def run(self):\n        mtimes = {}\n        while 1:\n            for filename in chain(_iter_module_files(), self.extra_files):\n                try:\n                    mtime = os.stat(filename).st_mtime\n                except OSError:\n                    continue\n\n                old_time = mtimes.get(filename)\n                if old_time is None:\n                    mtimes[filename] = mtime\n                    continue\n                elif mtime > old_time:\n                    self.trigger_reload(filename)\n            self._sleep(self.interval)\n\n\nclass WatchdogReloaderLoop(ReloaderLoop):\n    def __init__(self, *args, **kwargs):\n        ReloaderLoop.__init__(self, *args, **kwargs)\n        from watchdog.observers import Observer\n        from watchdog.events import FileSystemEventHandler\n\n        self.observable_paths = set()\n\n        def _check_modification(filename):\n            if filename in self.extra_files:\n                self.trigger_reload(filename)\n            dirname = os.path.dirname(filename)\n            if dirname.startswith(tuple(self.observable_paths)):\n                if filename.endswith((\".pyc\", \".pyo\", \".py\")):\n                    self.trigger_reload(filename)\n\n        class _CustomHandler(FileSystemEventHandler):\n            def on_created(self, event):\n                _check_modification(event.src_path)\n\n            def on_modified(self, event):\n                _check_modification(event.src_path)\n\n            def on_moved(self, event):\n                _check_modification(event.src_path)\n                _check_modification(event.dest_path)\n\n            def on_deleted(self, event):\n                _check_modification(event.src_path)\n\n        reloader_name = Observer.__name__.lower()\n        if reloader_name.endswith(\"observer\"):\n            reloader_name = reloader_name[:-8]\n        reloader_name += \" reloader\"\n\n        self.name = reloader_name\n\n        self.observer_class = Observer\n        self.event_handler = _CustomHandler()\n        self.should_reload = False\n\n    def trigger_reload(self, filename):\n        # This is called inside an event handler, which means throwing\n        # SystemExit has no effect.\n        # https://github.com/gorakhargosh/watchdog/issues/294\n        self.should_reload = True\n        self.log_reload(filename)\n\n    def run(self):\n        watches = {}\n        observer = self.observer_class()\n        observer.start()\n\n        try:\n            while not self.should_reload:\n                to_delete = set(watches)\n                paths = _find_observable_paths(self.extra_files)\n                for path in paths:\n                    if path not in watches:\n                        try:\n                            watches[path] = observer.schedule(\n                                self.event_handler, path, recursive=True\n                            )\n                        except OSError:\n                            # Clear this path from list of watches We don't want\n                            # the same error message showing again in the next\n                            # iteration.\n                            watches[path] = None\n                    to_delete.discard(path)\n                for path in to_delete:\n                    watch = watches.pop(path, None)\n                    if watch is not None:\n                        observer.unschedule(watch)\n                self.observable_paths = paths\n                self._sleep(self.interval)\n        finally:\n            observer.stop()\n            observer.join()\n\n        sys.exit(3)\n\n\nreloader_loops = {\"stat\": StatReloaderLoop, \"watchdog\": WatchdogReloaderLoop}\n\ntry:\n    __import__(\"watchdog.observers\")\nexcept ImportError:\n    reloader_loops[\"auto\"] = reloader_loops[\"stat\"]\nelse:\n    reloader_loops[\"auto\"] = reloader_loops[\"watchdog\"]\n\n\ndef ensure_echo_on():\n    \"\"\"Ensure that echo mode is enabled. Some tools such as PDB disable\n    it which causes usability issues after reload.\"\"\"\n    # tcgetattr will fail if stdin isn't a tty\n    if not sys.stdin.isatty():\n        return\n    try:\n        import termios\n    except ImportError:\n        return\n    attributes = termios.tcgetattr(sys.stdin)\n    if not attributes[3] & termios.ECHO:\n        attributes[3] |= termios.ECHO\n        termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes)\n\n\ndef run_with_reloader(main_func, extra_files=None, interval=1, reloader_type=\"auto\"):\n    \"\"\"Run the given function in an independent python interpreter.\"\"\"\n    import signal\n\n    reloader = reloader_loops[reloader_type](extra_files, interval)\n    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))\n    try:\n        if os.environ.get(\"WERKZEUG_RUN_MAIN\") == \"true\":\n            ensure_echo_on()\n            t = threading.Thread(target=main_func, args=())\n            t.setDaemon(True)\n            t.start()\n            reloader.run()\n        else:\n            sys.exit(reloader.restart_with_reloader())\n    except KeyboardInterrupt:\n        pass\n", "path": "src/werkzeug/_reloader.py"}], "after_files": [{"content": "import os\nimport subprocess\nimport sys\nimport threading\nimport time\nfrom itertools import chain\n\nfrom ._compat import iteritems\nfrom ._compat import PY2\nfrom ._compat import text_type\nfrom ._internal import _log\n\n\ndef _iter_module_files():\n    \"\"\"This iterates over all relevant Python files.  It goes through all\n    loaded files from modules, all files in folders of already loaded modules\n    as well as all files reachable through a package.\n    \"\"\"\n    # The list call is necessary on Python 3 in case the module\n    # dictionary modifies during iteration.\n    for module in list(sys.modules.values()):\n        if module is None:\n            continue\n        filename = getattr(module, \"__file__\", None)\n        if filename:\n            if os.path.isdir(filename) and os.path.exists(\n                os.path.join(filename, \"__init__.py\")\n            ):\n                filename = os.path.join(filename, \"__init__.py\")\n\n            old = None\n            while not os.path.isfile(filename):\n                old = filename\n                filename = os.path.dirname(filename)\n                if filename == old:\n                    break\n            else:\n                if filename[-4:] in (\".pyc\", \".pyo\"):\n                    filename = filename[:-1]\n                yield filename\n\n\ndef _find_observable_paths(extra_files=None):\n    \"\"\"Finds all paths that should be observed.\"\"\"\n    rv = set(\n        os.path.dirname(os.path.abspath(x)) if os.path.isfile(x) else os.path.abspath(x)\n        for x in sys.path\n    )\n\n    for filename in extra_files or ():\n        rv.add(os.path.dirname(os.path.abspath(filename)))\n\n    for module in list(sys.modules.values()):\n        fn = getattr(module, \"__file__\", None)\n        if fn is None:\n            continue\n        fn = os.path.abspath(fn)\n        rv.add(os.path.dirname(fn))\n\n    return _find_common_roots(rv)\n\n\ndef _get_args_for_reloading():\n    \"\"\"Determine how the script was executed, and return the args needed\n    to execute it again in a new process.\n    \"\"\"\n    rv = [sys.executable]\n    py_script = sys.argv[0]\n    args = sys.argv[1:]\n    # Need to look at main module to determine how it was executed.\n    __main__ = sys.modules[\"__main__\"]\n\n    # The value of __package__ indicates how Python was called. It may\n    # not exist if a setuptools script is installed as an egg.\n    if getattr(__main__, \"__package__\", None) is None:\n        # Executed a file, like \"python app.py\".\n        py_script = os.path.abspath(py_script)\n\n        if os.name == \"nt\":\n            # Windows entry points have \".exe\" extension and should be\n            # called directly.\n            if not os.path.exists(py_script) and os.path.exists(py_script + \".exe\"):\n                py_script += \".exe\"\n\n            if (\n                os.path.splitext(rv[0])[1] == \".exe\"\n                and os.path.splitext(py_script)[1] == \".exe\"\n            ):\n                rv.pop(0)\n\n        rv.append(py_script)\n    else:\n        # Executed a module, like \"python -m werkzeug.serving\".\n        if sys.argv[0] == \"-m\":\n            # Flask works around previous behavior by putting\n            # \"-m flask\" in sys.argv.\n            # TODO remove this once Flask no longer misbehaves\n            args = sys.argv\n        else:\n            if os.path.isfile(py_script):\n                # Rewritten by Python from \"-m script\" to \"/path/to/script.py\".\n                py_module = __main__.__package__\n                name = os.path.splitext(os.path.basename(py_script))[0]\n\n                if name != \"__main__\":\n                    py_module += \".\" + name\n            else:\n                # Incorrectly rewritten by pydevd debugger from \"-m script\" to \"script\".\n                py_module = py_script\n\n            rv.extend((\"-m\", py_module.lstrip(\".\")))\n\n    rv.extend(args)\n    return rv\n\n\ndef _find_common_roots(paths):\n    \"\"\"Out of some paths it finds the common roots that need monitoring.\"\"\"\n    paths = [x.split(os.path.sep) for x in paths]\n    root = {}\n    for chunks in sorted(paths, key=len, reverse=True):\n        node = root\n        for chunk in chunks:\n            node = node.setdefault(chunk, {})\n        node.clear()\n\n    rv = set()\n\n    def _walk(node, path):\n        for prefix, child in iteritems(node):\n            _walk(child, path + (prefix,))\n        if not node:\n            rv.add(\"/\".join(path))\n\n    _walk(root, ())\n    return rv\n\n\nclass ReloaderLoop(object):\n    name = None\n\n    # monkeypatched by testsuite. wrapping with `staticmethod` is required in\n    # case time.sleep has been replaced by a non-c function (e.g. by\n    # `eventlet.monkey_patch`) before we get here\n    _sleep = staticmethod(time.sleep)\n\n    def __init__(self, extra_files=None, interval=1):\n        self.extra_files = set(os.path.abspath(x) for x in extra_files or ())\n        self.interval = interval\n\n    def run(self):\n        pass\n\n    def restart_with_reloader(self):\n        \"\"\"Spawn a new Python interpreter with the same arguments as this one,\n        but running the reloader thread.\n        \"\"\"\n        while 1:\n            _log(\"info\", \" * Restarting with %s\" % self.name)\n            args = _get_args_for_reloading()\n\n            # a weird bug on windows. sometimes unicode strings end up in the\n            # environment and subprocess.call does not like this, encode them\n            # to latin1 and continue.\n            if os.name == \"nt\" and PY2:\n                new_environ = {}\n                for key, value in iteritems(os.environ):\n                    if isinstance(key, text_type):\n                        key = key.encode(\"iso-8859-1\")\n                    if isinstance(value, text_type):\n                        value = value.encode(\"iso-8859-1\")\n                    new_environ[key] = value\n            else:\n                new_environ = os.environ.copy()\n\n            new_environ[\"WERKZEUG_RUN_MAIN\"] = \"true\"\n            exit_code = subprocess.call(args, env=new_environ, close_fds=False)\n            if exit_code != 3:\n                return exit_code\n\n    def trigger_reload(self, filename):\n        self.log_reload(filename)\n        sys.exit(3)\n\n    def log_reload(self, filename):\n        filename = os.path.abspath(filename)\n        _log(\"info\", \" * Detected change in %r, reloading\" % filename)\n\n\nclass StatReloaderLoop(ReloaderLoop):\n    name = \"stat\"\n\n    def run(self):\n        mtimes = {}\n        while 1:\n            for filename in chain(_iter_module_files(), self.extra_files):\n                try:\n                    mtime = os.stat(filename).st_mtime\n                except OSError:\n                    continue\n\n                old_time = mtimes.get(filename)\n                if old_time is None:\n                    mtimes[filename] = mtime\n                    continue\n                elif mtime > old_time:\n                    self.trigger_reload(filename)\n            self._sleep(self.interval)\n\n\nclass WatchdogReloaderLoop(ReloaderLoop):\n    def __init__(self, *args, **kwargs):\n        ReloaderLoop.__init__(self, *args, **kwargs)\n        from watchdog.observers import Observer\n        from watchdog.events import FileSystemEventHandler\n\n        self.observable_paths = set()\n\n        def _check_modification(filename):\n            if filename in self.extra_files:\n                self.trigger_reload(filename)\n            dirname = os.path.dirname(filename)\n            if dirname.startswith(tuple(self.observable_paths)):\n                if filename.endswith((\".pyc\", \".pyo\", \".py\")):\n                    self.trigger_reload(filename)\n\n        class _CustomHandler(FileSystemEventHandler):\n            def on_created(self, event):\n                _check_modification(event.src_path)\n\n            def on_modified(self, event):\n                _check_modification(event.src_path)\n\n            def on_moved(self, event):\n                _check_modification(event.src_path)\n                _check_modification(event.dest_path)\n\n            def on_deleted(self, event):\n                _check_modification(event.src_path)\n\n        reloader_name = Observer.__name__.lower()\n        if reloader_name.endswith(\"observer\"):\n            reloader_name = reloader_name[:-8]\n        reloader_name += \" reloader\"\n\n        self.name = reloader_name\n\n        self.observer_class = Observer\n        self.event_handler = _CustomHandler()\n        self.should_reload = False\n\n    def trigger_reload(self, filename):\n        # This is called inside an event handler, which means throwing\n        # SystemExit has no effect.\n        # https://github.com/gorakhargosh/watchdog/issues/294\n        self.should_reload = True\n        self.log_reload(filename)\n\n    def run(self):\n        watches = {}\n        observer = self.observer_class()\n        observer.start()\n\n        try:\n            while not self.should_reload:\n                to_delete = set(watches)\n                paths = _find_observable_paths(self.extra_files)\n                for path in paths:\n                    if path not in watches:\n                        try:\n                            watches[path] = observer.schedule(\n                                self.event_handler, path, recursive=True\n                            )\n                        except OSError:\n                            # Clear this path from list of watches We don't want\n                            # the same error message showing again in the next\n                            # iteration.\n                            watches[path] = None\n                    to_delete.discard(path)\n                for path in to_delete:\n                    watch = watches.pop(path, None)\n                    if watch is not None:\n                        observer.unschedule(watch)\n                self.observable_paths = paths\n                self._sleep(self.interval)\n        finally:\n            observer.stop()\n            observer.join()\n\n        sys.exit(3)\n\n\nreloader_loops = {\"stat\": StatReloaderLoop, \"watchdog\": WatchdogReloaderLoop}\n\ntry:\n    __import__(\"watchdog.observers\")\nexcept ImportError:\n    reloader_loops[\"auto\"] = reloader_loops[\"stat\"]\nelse:\n    reloader_loops[\"auto\"] = reloader_loops[\"watchdog\"]\n\n\ndef ensure_echo_on():\n    \"\"\"Ensure that echo mode is enabled. Some tools such as PDB disable\n    it which causes usability issues after reload.\"\"\"\n    # tcgetattr will fail if stdin isn't a tty\n    if not sys.stdin.isatty():\n        return\n    try:\n        import termios\n    except ImportError:\n        return\n    attributes = termios.tcgetattr(sys.stdin)\n    if not attributes[3] & termios.ECHO:\n        attributes[3] |= termios.ECHO\n        termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes)\n\n\ndef run_with_reloader(main_func, extra_files=None, interval=1, reloader_type=\"auto\"):\n    \"\"\"Run the given function in an independent python interpreter.\"\"\"\n    import signal\n\n    reloader = reloader_loops[reloader_type](extra_files, interval)\n    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))\n    try:\n        if os.environ.get(\"WERKZEUG_RUN_MAIN\") == \"true\":\n            ensure_echo_on()\n            t = threading.Thread(target=main_func, args=())\n            t.setDaemon(True)\n            t.start()\n            reloader.run()\n        else:\n            sys.exit(reloader.restart_with_reloader())\n    except KeyboardInterrupt:\n        pass\n", "path": "src/werkzeug/_reloader.py"}]}