gradio
1# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
2# Copyright (c) Yuichiro Tachibana (2023)
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import fnmatch17import logging18import os19import sys20import types21from typing import Optional, Set22
23LOGGER = logging.getLogger(__name__)24
25#
26# Copied from https://github.com/streamlit/streamlit/blob/1.24.0/lib/streamlit/file_util.py
27#
28
29def file_is_in_folder_glob(filepath, folderpath_glob) -> bool:30"""Test whether a file is in some folder with globbing support.31
32Parameters
33----------
34filepath : str
35A file path.
36folderpath_glob: str
37A path to a folder that may include globbing.
38
39"""
40# Make the glob always end with "/*" so we match files inside subfolders of41# folderpath_glob.42if not folderpath_glob.endswith("*"):43if folderpath_glob.endswith("/"):44folderpath_glob += "*"45else:46folderpath_glob += "/*"47
48file_dir = os.path.dirname(filepath) + "/"49return fnmatch.fnmatch(file_dir, folderpath_glob)50
51
52def get_directory_size(directory: str) -> int:53"""Return the size of a directory in bytes."""54total_size = 055for dirpath, _, filenames in os.walk(directory):56for f in filenames:57fp = os.path.join(dirpath, f)58total_size += os.path.getsize(fp)59return total_size60
61
62def file_in_pythonpath(filepath) -> bool:63"""Test whether a filepath is in the same folder of a path specified in the PYTHONPATH env variable.64
65
66Parameters
67----------
68filepath : str
69An absolute file path.
70
71Returns
72-------
73boolean
74True if contained in PYTHONPATH, False otherwise. False if PYTHONPATH is not defined or empty.
75
76"""
77pythonpath = os.environ.get("PYTHONPATH", "")78if len(pythonpath) == 0:79return False80
81absolute_paths = [os.path.abspath(path) for path in pythonpath.split(os.pathsep)]82return any(83file_is_in_folder_glob(os.path.normpath(filepath), path)84for path in absolute_paths85)86
87#
88# Copied from https://github.com/streamlit/streamlit/blob/1.24.0/lib/streamlit/watcher/local_sources_watcher.py
89#
90
91def get_module_paths(module: types.ModuleType) -> Set[str]:92paths_extractors = [93# https://docs.python.org/3/reference/datamodel.html94# __file__ is the pathname of the file from which the module was loaded95# if it was loaded from a file.96# The __file__ attribute may be missing for certain types of modules97lambda m: [m.__file__],98# https://docs.python.org/3/reference/import.html#__spec__99# The __spec__ attribute is set to the module spec that was used100# when importing the module. one exception is __main__,101# where __spec__ is set to None in some cases.102# https://www.python.org/dev/peps/pep-0451/#id16103# "origin" in an import context means the system104# (or resource within a system) from which a module originates105# ... It is up to the loader to decide on how to interpret106# and use a module's origin, if at all.107lambda m: [m.__spec__.origin],108# https://www.python.org/dev/peps/pep-0420/109# Handling of "namespace packages" in which the __path__ attribute110# is a _NamespacePath object with a _path attribute containing111# the various paths of the package.112lambda m: list(m.__path__._path),113]114
115all_paths = set()116for extract_paths in paths_extractors:117potential_paths = []118try:119potential_paths = extract_paths(module)120except AttributeError:121# Some modules might not have __file__ or __spec__ attributes.122pass123except Exception as e:124LOGGER.warning(f"Examining the path of {module.__name__} raised: {e}")125
126all_paths.update(127[os.path.abspath(str(p)) for p in potential_paths if _is_valid_path(p)]128)129return all_paths130
131
132def _is_valid_path(path: Optional[str]) -> bool:133return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))134
135
136#
137# Original code
138#
139
140def unload_local_modules(target_dir_path: str = "."):141""" Unload all modules that are in the target directory or in a subdirectory of it.142It is necessary to unload modules before re-executing a script that imports the modules,
143so that the new version of the modules is loaded.
144The module unloading feature is extracted from Streamlit's LocalSourcesWatcher (https://github.com/streamlit/streamlit/blob/1.24.0/lib/streamlit/watcher/local_sources_watcher.py)
145and packaged as a standalone function.
146"""
147target_dir_path = os.path.abspath(target_dir_path)148loaded_modules = {} # filepath -> module_name149
150# Copied from `LocalSourcesWatcher.update_watched_modules()`151module_paths = {152name: get_module_paths(module)153for name, module in dict(sys.modules).items()154}155
156# Copied from `LocalSourcesWatcher._register_necessary_watchers()`157for name, paths in module_paths.items():158for path in paths:159if file_is_in_folder_glob(path, target_dir_path) or file_in_pythonpath(path):160loaded_modules[path] = name161
162# Copied from `LocalSourcesWatcher.on_file_changed()`163for module_name in loaded_modules.values():164if module_name is not None and module_name in sys.modules:165del sys.modules[module_name]166