We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Auto-reload doesn't work with subdirs
### Summary

Auto-reload doesn’t work if both the app file and a secondary module are in the same subdir. While this doesn't affect app behavior, it's a pretty critical bug because you need to manually restart Streamlit all the time in this configuration.


### Steps to reproduce

Create a project dir like this:

    .
    |- subdir
       |- streamlit_app.py  # this imports secondary
       |- secondary.py

And then run:

    streamlit run subdir/streamlit_app.py

This will run the app but it won’t show a reload prompt when secondary.py changes. Instead, you need to manually rerun the Streamlit app. See also this [discussion on Slack](https://streamlit.slack.com/archives/C019AE89C2C/p1627346650027100). (Btw if streamlit_app.py is in root and only secondary.py is in subdir, this works).


### Debug info

- Streamlit version: 0.87
- Python version: 3.8
- Pipenv
- OS version: MacOS
- Browser version: Chrome



--- 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/streamlit/watcher/local_sources_watcher.py`
Content:
```
1 # Copyright 2018-2021 Streamlit Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import os
16 import sys
17 import collections
18 import typing as t
19 import types
20 
21 from streamlit import config
22 from streamlit import file_util
23 from streamlit.folder_black_list import FolderBlackList
24 
25 from streamlit.logger import get_logger
26 from streamlit.watcher.file_watcher import (
27     get_default_file_watcher_class,
28     NoOpFileWatcher,
29 )
30 
31 LOGGER = get_logger(__name__)
32 
33 WatchedModule = collections.namedtuple("WatchedModule", ["watcher", "module_name"])
34 
35 # This needs to be initialized lazily to avoid calling config.get_option() and
36 # thus initializing config options when this file is first imported.
37 FileWatcher = None
38 
39 
40 class LocalSourcesWatcher(object):
41     def __init__(self, report, on_file_changed):
42         self._report = report
43         self._on_file_changed = on_file_changed
44         self._is_closed = False
45 
46         # Blacklist for folders that should not be watched
47         self._folder_black_list = FolderBlackList(
48             config.get_option("server.folderWatchBlacklist")
49         )
50 
51         # A dict of filepath -> WatchedModule.
52         self._watched_modules = {}
53 
54         self._register_watcher(
55             self._report.script_path,
56             module_name=None,  # Only the root script has None here.
57         )
58 
59     def on_file_changed(self, filepath):
60         if filepath not in self._watched_modules:
61             LOGGER.error("Received event for non-watched file: %s", filepath)
62             return
63 
64         # Workaround:
65         # Delete all watched modules so we can guarantee changes to the
66         # updated module are reflected on reload.
67         #
68         # In principle, for reloading a given module, we only need to unload
69         # the module itself and all of the modules which import it (directly
70         # or indirectly) such that when we exec the application code, the
71         # changes are reloaded and reflected in the running application.
72         #
73         # However, determining all import paths for a given loaded module is
74         # non-trivial, and so as a workaround we simply unload all watched
75         # modules.
76         for wm in self._watched_modules.values():
77             if wm.module_name is not None and wm.module_name in sys.modules:
78                 del sys.modules[wm.module_name]
79 
80         self._on_file_changed()
81 
82     def close(self):
83         for wm in self._watched_modules.values():
84             wm.watcher.close()
85         self._watched_modules = {}
86         self._is_closed = True
87 
88     def _register_watcher(self, filepath, module_name):
89         global FileWatcher
90         if FileWatcher is None:
91             FileWatcher = get_default_file_watcher_class()
92 
93         if FileWatcher is NoOpFileWatcher:
94             return
95 
96         try:
97             wm = WatchedModule(
98                 watcher=FileWatcher(filepath, self.on_file_changed),
99                 module_name=module_name,
100             )
101         except PermissionError:
102             # If you don't have permission to read this file, don't even add it
103             # to watchers.
104             return
105 
106         self._watched_modules[filepath] = wm
107 
108     def _deregister_watcher(self, filepath):
109         if filepath not in self._watched_modules:
110             return
111 
112         if filepath == self._report.script_path:
113             return
114 
115         wm = self._watched_modules[filepath]
116         wm.watcher.close()
117         del self._watched_modules[filepath]
118 
119     def _file_is_new(self, filepath):
120         return filepath not in self._watched_modules
121 
122     def _file_should_be_watched(self, filepath):
123         # Using short circuiting for performance.
124         return self._file_is_new(filepath) and (
125             file_util.file_is_in_folder_glob(filepath, self._report.script_folder)
126             or file_util.file_in_pythonpath(filepath)
127         )
128 
129     def update_watched_modules(self):
130         if self._is_closed:
131             return
132 
133         modules_paths = {
134             name: self._exclude_blacklisted_paths(get_module_paths(module))
135             for name, module in dict(sys.modules).items()
136         }
137 
138         self._register_necessary_watchers(modules_paths)
139 
140     def _register_necessary_watchers(
141         self, module_paths: t.Dict[str, t.Set[str]]
142     ) -> None:
143         for name, paths in module_paths.items():
144             for path in paths:
145                 if self._file_should_be_watched(path):
146                     self._register_watcher(path, name)
147 
148     def _exclude_blacklisted_paths(self, paths: t.Set[str]) -> t.Set[str]:
149         return {p for p in paths if not self._folder_black_list.is_blacklisted(p)}
150 
151 
152 def get_module_paths(module: types.ModuleType) -> t.Set[str]:
153     paths_extractors = [
154         # https://docs.python.org/3/reference/datamodel.html
155         # __file__ is the pathname of the file from which the module was loaded
156         # if it was loaded from a file.
157         # The __file__ attribute may be missing for certain types of modules
158         lambda m: [m.__file__],
159         # https://docs.python.org/3/reference/import.html#__spec__
160         # The __spec__ attribute is set to the module spec that was used
161         # when importing the module. one exception is __main__,
162         # where __spec__ is set to None in some cases.
163         # https://www.python.org/dev/peps/pep-0451/#id16
164         # "origin" in an import context means the system
165         # (or resource within a system) from which a module originates
166         # ... It is up to the loader to decide on how to interpret
167         # and use a module's origin, if at all.
168         lambda m: [m.__spec__.origin],
169         # https://www.python.org/dev/peps/pep-0420/
170         # Handling of "namespace packages" in which the __path__ attribute
171         # is a _NamespacePath object with a _path attribute containing
172         # the various paths of the package.
173         lambda m: [p for p in m.__path__._path],
174     ]
175 
176     all_paths = set()
177     for extract_paths in paths_extractors:
178         potential_paths = []
179         try:
180             potential_paths = extract_paths(module)
181         except AttributeError:
182             pass
183         except Exception as e:
184             LOGGER.warning(f"Examining the path of {module.__name__} raised: {e}")
185 
186         all_paths.update([str(p) for p in potential_paths if _is_valid_path(p)])
187     return all_paths
188 
189 
190 def _is_valid_path(path: t.Optional[str]) -> bool:
191     return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))
192
```
--- 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/streamlit/watcher/local_sources_watcher.py b/lib/streamlit/watcher/local_sources_watcher.py
--- a/lib/streamlit/watcher/local_sources_watcher.py
+++ b/lib/streamlit/watcher/local_sources_watcher.py
@@ -183,7 +183,9 @@
         except Exception as e:
             LOGGER.warning(f"Examining the path of {module.__name__} raised: {e}")
 
-        all_paths.update([str(p) for p in potential_paths if _is_valid_path(p)])
+        all_paths.update(
+            [os.path.abspath(str(p)) for p in potential_paths if _is_valid_path(p)]
+        )
     return all_paths

{"golden_diff": "diff --git a/lib/streamlit/watcher/local_sources_watcher.py b/lib/streamlit/watcher/local_sources_watcher.py\n--- a/lib/streamlit/watcher/local_sources_watcher.py\n+++ b/lib/streamlit/watcher/local_sources_watcher.py\n@@ -183,7 +183,9 @@\n         except Exception as e:\n             LOGGER.warning(f\"Examining the path of {module.__name__} raised: {e}\")\n \n-        all_paths.update([str(p) for p in potential_paths if _is_valid_path(p)])\n+        all_paths.update(\n+            [os.path.abspath(str(p)) for p in potential_paths if _is_valid_path(p)]\n+        )\n     return all_paths\n", "issue": "Auto-reload doesn't work with subdirs\n### Summary\r\n\r\nAuto-reload doesn\u2019t work if both the app file and a secondary module are in the same subdir. While this doesn't affect app behavior, it's a pretty critical bug because you need to manually restart Streamlit all the time in this configuration.\r\n\r\n\r\n### Steps to reproduce\r\n\r\nCreate a project dir like this:\r\n\r\n    .\r\n    |- subdir\r\n       |- streamlit_app.py  # this imports secondary\r\n       |- secondary.py\r\n\r\nAnd then run:\r\n\r\n    streamlit run subdir/streamlit_app.py\r\n\r\nThis will run the app but it won\u2019t show a reload prompt when secondary.py changes. Instead, you need to manually rerun the Streamlit app. See also this [discussion on Slack](https://streamlit.slack.com/archives/C019AE89C2C/p1627346650027100). (Btw if streamlit_app.py is in root and only secondary.py is in subdir, this works).\r\n\r\n\r\n### Debug info\r\n\r\n- Streamlit version: 0.87\r\n- Python version: 3.8\r\n- Pipenv\r\n- OS version: MacOS\r\n- Browser version: Chrome\r\n\r\n\n", "before_files": [{"content": "# Copyright 2018-2021 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\nimport collections\nimport typing as t\nimport types\n\nfrom streamlit import config\nfrom streamlit import file_util\nfrom streamlit.folder_black_list import FolderBlackList\n\nfrom streamlit.logger import get_logger\nfrom streamlit.watcher.file_watcher import (\n    get_default_file_watcher_class,\n    NoOpFileWatcher,\n)\n\nLOGGER = get_logger(__name__)\n\nWatchedModule = collections.namedtuple(\"WatchedModule\", [\"watcher\", \"module_name\"])\n\n# This needs to be initialized lazily to avoid calling config.get_option() and\n# thus initializing config options when this file is first imported.\nFileWatcher = None\n\n\nclass LocalSourcesWatcher(object):\n    def __init__(self, report, on_file_changed):\n        self._report = report\n        self._on_file_changed = on_file_changed\n        self._is_closed = False\n\n        # Blacklist for folders that should not be watched\n        self._folder_black_list = FolderBlackList(\n            config.get_option(\"server.folderWatchBlacklist\")\n        )\n\n        # A dict of filepath -> WatchedModule.\n        self._watched_modules = {}\n\n        self._register_watcher(\n            self._report.script_path,\n            module_name=None,  # Only the root script has None here.\n        )\n\n    def on_file_changed(self, filepath):\n        if filepath not in self._watched_modules:\n            LOGGER.error(\"Received event for non-watched file: %s\", filepath)\n            return\n\n        # Workaround:\n        # Delete all watched modules so we can guarantee changes to the\n        # updated module are reflected on reload.\n        #\n        # In principle, for reloading a given module, we only need to unload\n        # the module itself and all of the modules which import it (directly\n        # or indirectly) such that when we exec the application code, the\n        # changes are reloaded and reflected in the running application.\n        #\n        # However, determining all import paths for a given loaded module is\n        # non-trivial, and so as a workaround we simply unload all watched\n        # modules.\n        for wm in self._watched_modules.values():\n            if wm.module_name is not None and wm.module_name in sys.modules:\n                del sys.modules[wm.module_name]\n\n        self._on_file_changed()\n\n    def close(self):\n        for wm in self._watched_modules.values():\n            wm.watcher.close()\n        self._watched_modules = {}\n        self._is_closed = True\n\n    def _register_watcher(self, filepath, module_name):\n        global FileWatcher\n        if FileWatcher is None:\n            FileWatcher = get_default_file_watcher_class()\n\n        if FileWatcher is NoOpFileWatcher:\n            return\n\n        try:\n            wm = WatchedModule(\n                watcher=FileWatcher(filepath, self.on_file_changed),\n                module_name=module_name,\n            )\n        except PermissionError:\n            # If you don't have permission to read this file, don't even add it\n            # to watchers.\n            return\n\n        self._watched_modules[filepath] = wm\n\n    def _deregister_watcher(self, filepath):\n        if filepath not in self._watched_modules:\n            return\n\n        if filepath == self._report.script_path:\n            return\n\n        wm = self._watched_modules[filepath]\n        wm.watcher.close()\n        del self._watched_modules[filepath]\n\n    def _file_is_new(self, filepath):\n        return filepath not in self._watched_modules\n\n    def _file_should_be_watched(self, filepath):\n        # Using short circuiting for performance.\n        return self._file_is_new(filepath) and (\n            file_util.file_is_in_folder_glob(filepath, self._report.script_folder)\n            or file_util.file_in_pythonpath(filepath)\n        )\n\n    def update_watched_modules(self):\n        if self._is_closed:\n            return\n\n        modules_paths = {\n            name: self._exclude_blacklisted_paths(get_module_paths(module))\n            for name, module in dict(sys.modules).items()\n        }\n\n        self._register_necessary_watchers(modules_paths)\n\n    def _register_necessary_watchers(\n        self, module_paths: t.Dict[str, t.Set[str]]\n    ) -> None:\n        for name, paths in module_paths.items():\n            for path in paths:\n                if self._file_should_be_watched(path):\n                    self._register_watcher(path, name)\n\n    def _exclude_blacklisted_paths(self, paths: t.Set[str]) -> t.Set[str]:\n        return {p for p in paths if not self._folder_black_list.is_blacklisted(p)}\n\n\ndef get_module_paths(module: types.ModuleType) -> t.Set[str]:\n    paths_extractors = [\n        # https://docs.python.org/3/reference/datamodel.html\n        # __file__ is the pathname of the file from which the module was loaded\n        # if it was loaded from a file.\n        # The __file__ attribute may be missing for certain types of modules\n        lambda m: [m.__file__],\n        # https://docs.python.org/3/reference/import.html#__spec__\n        # The __spec__ attribute is set to the module spec that was used\n        # when importing the module. one exception is __main__,\n        # where __spec__ is set to None in some cases.\n        # https://www.python.org/dev/peps/pep-0451/#id16\n        # \"origin\" in an import context means the system\n        # (or resource within a system) from which a module originates\n        # ... It is up to the loader to decide on how to interpret\n        # and use a module's origin, if at all.\n        lambda m: [m.__spec__.origin],\n        # https://www.python.org/dev/peps/pep-0420/\n        # Handling of \"namespace packages\" in which the __path__ attribute\n        # is a _NamespacePath object with a _path attribute containing\n        # the various paths of the package.\n        lambda m: [p for p in m.__path__._path],\n    ]\n\n    all_paths = set()\n    for extract_paths in paths_extractors:\n        potential_paths = []\n        try:\n            potential_paths = extract_paths(module)\n        except AttributeError:\n            pass\n        except Exception as e:\n            LOGGER.warning(f\"Examining the path of {module.__name__} raised: {e}\")\n\n        all_paths.update([str(p) for p in potential_paths if _is_valid_path(p)])\n    return all_paths\n\n\ndef _is_valid_path(path: t.Optional[str]) -> bool:\n    return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))\n", "path": "lib/streamlit/watcher/local_sources_watcher.py"}], "after_files": [{"content": "# Copyright 2018-2021 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\nimport collections\nimport typing as t\nimport types\n\nfrom streamlit import config\nfrom streamlit import file_util\nfrom streamlit.folder_black_list import FolderBlackList\n\nfrom streamlit.logger import get_logger\nfrom streamlit.watcher.file_watcher import (\n    get_default_file_watcher_class,\n    NoOpFileWatcher,\n)\n\nLOGGER = get_logger(__name__)\n\nWatchedModule = collections.namedtuple(\"WatchedModule\", [\"watcher\", \"module_name\"])\n\n# This needs to be initialized lazily to avoid calling config.get_option() and\n# thus initializing config options when this file is first imported.\nFileWatcher = None\n\n\nclass LocalSourcesWatcher(object):\n    def __init__(self, report, on_file_changed):\n        self._report = report\n        self._on_file_changed = on_file_changed\n        self._is_closed = False\n\n        # Blacklist for folders that should not be watched\n        self._folder_black_list = FolderBlackList(\n            config.get_option(\"server.folderWatchBlacklist\")\n        )\n\n        # A dict of filepath -> WatchedModule.\n        self._watched_modules = {}\n\n        self._register_watcher(\n            self._report.script_path,\n            module_name=None,  # Only the root script has None here.\n        )\n\n    def on_file_changed(self, filepath):\n        if filepath not in self._watched_modules:\n            LOGGER.error(\"Received event for non-watched file: %s\", filepath)\n            return\n\n        # Workaround:\n        # Delete all watched modules so we can guarantee changes to the\n        # updated module are reflected on reload.\n        #\n        # In principle, for reloading a given module, we only need to unload\n        # the module itself and all of the modules which import it (directly\n        # or indirectly) such that when we exec the application code, the\n        # changes are reloaded and reflected in the running application.\n        #\n        # However, determining all import paths for a given loaded module is\n        # non-trivial, and so as a workaround we simply unload all watched\n        # modules.\n        for wm in self._watched_modules.values():\n            if wm.module_name is not None and wm.module_name in sys.modules:\n                del sys.modules[wm.module_name]\n\n        self._on_file_changed()\n\n    def close(self):\n        for wm in self._watched_modules.values():\n            wm.watcher.close()\n        self._watched_modules = {}\n        self._is_closed = True\n\n    def _register_watcher(self, filepath, module_name):\n        global FileWatcher\n        if FileWatcher is None:\n            FileWatcher = get_default_file_watcher_class()\n\n        if FileWatcher is NoOpFileWatcher:\n            return\n\n        try:\n            wm = WatchedModule(\n                watcher=FileWatcher(filepath, self.on_file_changed),\n                module_name=module_name,\n            )\n        except PermissionError:\n            # If you don't have permission to read this file, don't even add it\n            # to watchers.\n            return\n\n        self._watched_modules[filepath] = wm\n\n    def _deregister_watcher(self, filepath):\n        if filepath not in self._watched_modules:\n            return\n\n        if filepath == self._report.script_path:\n            return\n\n        wm = self._watched_modules[filepath]\n        wm.watcher.close()\n        del self._watched_modules[filepath]\n\n    def _file_is_new(self, filepath):\n        return filepath not in self._watched_modules\n\n    def _file_should_be_watched(self, filepath):\n        # Using short circuiting for performance.\n        return self._file_is_new(filepath) and (\n            file_util.file_is_in_folder_glob(filepath, self._report.script_folder)\n            or file_util.file_in_pythonpath(filepath)\n        )\n\n    def update_watched_modules(self):\n        if self._is_closed:\n            return\n\n        modules_paths = {\n            name: self._exclude_blacklisted_paths(get_module_paths(module))\n            for name, module in dict(sys.modules).items()\n        }\n\n        self._register_necessary_watchers(modules_paths)\n\n    def _register_necessary_watchers(\n        self, module_paths: t.Dict[str, t.Set[str]]\n    ) -> None:\n        for name, paths in module_paths.items():\n            for path in paths:\n                if self._file_should_be_watched(path):\n                    self._register_watcher(path, name)\n\n    def _exclude_blacklisted_paths(self, paths: t.Set[str]) -> t.Set[str]:\n        return {p for p in paths if not self._folder_black_list.is_blacklisted(p)}\n\n\ndef get_module_paths(module: types.ModuleType) -> t.Set[str]:\n    paths_extractors = [\n        # https://docs.python.org/3/reference/datamodel.html\n        # __file__ is the pathname of the file from which the module was loaded\n        # if it was loaded from a file.\n        # The __file__ attribute may be missing for certain types of modules\n        lambda m: [m.__file__],\n        # https://docs.python.org/3/reference/import.html#__spec__\n        # The __spec__ attribute is set to the module spec that was used\n        # when importing the module. one exception is __main__,\n        # where __spec__ is set to None in some cases.\n        # https://www.python.org/dev/peps/pep-0451/#id16\n        # \"origin\" in an import context means the system\n        # (or resource within a system) from which a module originates\n        # ... It is up to the loader to decide on how to interpret\n        # and use a module's origin, if at all.\n        lambda m: [m.__spec__.origin],\n        # https://www.python.org/dev/peps/pep-0420/\n        # Handling of \"namespace packages\" in which the __path__ attribute\n        # is a _NamespacePath object with a _path attribute containing\n        # the various paths of the package.\n        lambda m: [p for p in m.__path__._path],\n    ]\n\n    all_paths = set()\n    for extract_paths in paths_extractors:\n        potential_paths = []\n        try:\n            potential_paths = extract_paths(module)\n        except AttributeError:\n            pass\n        except Exception as e:\n            LOGGER.warning(f\"Examining the path of {module.__name__} raised: {e}\")\n\n        all_paths.update(\n            [os.path.abspath(str(p)) for p in potential_paths if _is_valid_path(p)]\n        )\n    return all_paths\n\n\ndef _is_valid_path(path: t.Optional[str]) -> bool:\n    return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))\n", "path": "lib/streamlit/watcher/local_sources_watcher.py"}]}