CoolFace
Apppublic

DoruC/Grounded-Segment-Anything

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
dynamic_module_utils.py625 linesDownload Raw Back to transformers_4_35_0
1# coding=utf-82# Copyright 2021 The HuggingFace Inc. team.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 at7#8#     http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# 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 and14# limitations under the License.15"""Utilities to dynamically load objects from the Hub."""16import filecmp17import importlib18import os19import re20import shutil21import signal22import sys23import typing24import warnings25from pathlib import Path26from typing import Any, Dict, List, Optional, Union27 28from .utils import (29    HF_MODULES_CACHE,30    TRANSFORMERS_DYNAMIC_MODULE_NAME,31    cached_file,32    extract_commit_hash,33    is_offline_mode,34    logging,35    try_to_load_from_cache,36)37 38 39logger = logging.get_logger(__name__)  # pylint: disable=invalid-name40 41 42def init_hf_modules():43    """44    Creates the cache directory for modules with an init, and adds it to the Python path.45    """46    # This function has already been executed if HF_MODULES_CACHE already is in the Python path.47    if HF_MODULES_CACHE in sys.path:48        return49 50    sys.path.append(HF_MODULES_CACHE)51    os.makedirs(HF_MODULES_CACHE, exist_ok=True)52    init_path = Path(HF_MODULES_CACHE) / "__init__.py"53    if not init_path.exists():54        init_path.touch()55        importlib.invalidate_caches()56 57 58def create_dynamic_module(name: Union[str, os.PathLike]):59    """60    Creates a dynamic module in the cache directory for modules.61 62    Args:63        name (`str` or `os.PathLike`):64            The name of the dynamic module to create.65    """66    init_hf_modules()67    dynamic_module_path = (Path(HF_MODULES_CACHE) / name).resolve()68    # If the parent module does not exist yet, recursively create it.69    if not dynamic_module_path.parent.exists():70        create_dynamic_module(dynamic_module_path.parent)71    os.makedirs(dynamic_module_path, exist_ok=True)72    init_path = dynamic_module_path / "__init__.py"73    if not init_path.exists():74        init_path.touch()75        # It is extremely important to invalidate the cache when we change stuff in those modules, or users end up76        # with errors about module that do not exist. Same for all other `invalidate_caches` in this file.77        importlib.invalidate_caches()78 79 80def get_relative_imports(module_file: Union[str, os.PathLike]) -> List[str]:81    """82    Get the list of modules that are relatively imported in a module file.83 84    Args:85        module_file (`str` or `os.PathLike`): The module file to inspect.86 87    Returns:88        `List[str]`: The list of relative imports in the module.89    """90    with open(module_file, "r", encoding="utf-8") as f:91        content = f.read()92 93    # Imports of the form `import .xxx`94    relative_imports = re.findall(r"^\s*import\s+\.(\S+)\s*$", content, flags=re.MULTILINE)95    # Imports of the form `from .xxx import yyy`96    relative_imports += re.findall(r"^\s*from\s+\.(\S+)\s+import", content, flags=re.MULTILINE)97    # Unique-ify98    return list(set(relative_imports))99 100 101def get_relative_import_files(module_file: Union[str, os.PathLike]) -> List[str]:102    """103    Get the list of all files that are needed for a given module. Note that this function recurses through the relative104    imports (if a imports b and b imports c, it will return module files for b and c).105 106    Args:107        module_file (`str` or `os.PathLike`): The module file to inspect.108 109    Returns:110        `List[str]`: The list of all relative imports a given module needs (recursively), which will give us the list111        of module files a given module needs.112    """113    no_change = False114    files_to_check = [module_file]115    all_relative_imports = []116 117    # Let's recurse through all relative imports118    while not no_change:119        new_imports = []120        for f in files_to_check:121            new_imports.extend(get_relative_imports(f))122 123        module_path = Path(module_file).parent124        new_import_files = [str(module_path / m) for m in new_imports]125        new_import_files = [f for f in new_import_files if f not in all_relative_imports]126        files_to_check = [f"{f}.py" for f in new_import_files]127 128        no_change = len(new_import_files) == 0129        all_relative_imports.extend(files_to_check)130 131    return all_relative_imports132 133 134def get_imports(filename: Union[str, os.PathLike]) -> List[str]:135    """136    Extracts all the libraries (not relative imports this time) that are imported in a file.137 138    Args:139        filename (`str` or `os.PathLike`): The module file to inspect.140 141    Returns:142        `List[str]`: The list of all packages required to use the input module.143    """144    with open(filename, "r", encoding="utf-8") as f:145        content = f.read()146 147    # filter out try/except block so in custom code we can have try/except imports148    content = re.sub(r"\s*try\s*:\s*.*?\s*except\s*.*?:", "", content, flags=re.MULTILINE | re.DOTALL)149 150    # Imports of the form `import xxx`151    imports = re.findall(r"^\s*import\s+(\S+)\s*$", content, flags=re.MULTILINE)152    # Imports of the form `from xxx import yyy`153    imports += re.findall(r"^\s*from\s+(\S+)\s+import", content, flags=re.MULTILINE)154    # Only keep the top-level module155    imports = [imp.split(".")[0] for imp in imports if not imp.startswith(".")]156    return list(set(imports))157 158 159def check_imports(filename: Union[str, os.PathLike]) -> List[str]:160    """161    Check if the current Python environment contains all the libraries that are imported in a file. Will raise if a162    library is missing.163 164    Args:165        filename (`str` or `os.PathLike`): The module file to check.166 167    Returns:168        `List[str]`: The list of relative imports in the file.169    """170    imports = get_imports(filename)171    missing_packages = []172    for imp in imports:173        try:174            importlib.import_module(imp)175        except ImportError:176            missing_packages.append(imp)177 178    if len(missing_packages) > 0:179        raise ImportError(180            "This modeling file requires the following packages that were not found in your environment: "181            f"{', '.join(missing_packages)}. Run `pip install {' '.join(missing_packages)}`"182        )183 184    return get_relative_imports(filename)185 186 187def get_class_in_module(class_name: str, module_path: Union[str, os.PathLike]) -> typing.Type:188    """189    Import a module on the cache directory for modules and extract a class from it.190 191    Args:192        class_name (`str`): The name of the class to import.193        module_path (`str` or `os.PathLike`): The path to the module to import.194 195    Returns:196        `typing.Type`: The class looked for.197    """198    module_path = module_path.replace(os.path.sep, ".")199    module = importlib.import_module(module_path)200    return getattr(module, class_name)201 202 203def get_cached_module_file(204    pretrained_model_name_or_path: Union[str, os.PathLike],205    module_file: str,206    cache_dir: Optional[Union[str, os.PathLike]] = None,207    force_download: bool = False,208    resume_download: bool = False,209    proxies: Optional[Dict[str, str]] = None,210    token: Optional[Union[bool, str]] = None,211    revision: Optional[str] = None,212    local_files_only: bool = False,213    repo_type: Optional[str] = None,214    _commit_hash: Optional[str] = None,215    **deprecated_kwargs,216) -> str:217    """218    Prepares Downloads a module from a local folder or a distant repo and returns its path inside the cached219    Transformers module.220 221    Args:222        pretrained_model_name_or_path (`str` or `os.PathLike`):223            This can be either:224 225            - a string, the *model id* of a pretrained model configuration hosted inside a model repo on226              huggingface.co. Valid model ids can be located at the root-level, like `bert-base-uncased`, or namespaced227              under a user or organization name, like `dbmdz/bert-base-german-cased`.228            - a path to a *directory* containing a configuration file saved using the229              [`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.230 231        module_file (`str`):232            The name of the module file containing the class to look for.233        cache_dir (`str` or `os.PathLike`, *optional*):234            Path to a directory in which a downloaded pretrained model configuration should be cached if the standard235            cache should not be used.236        force_download (`bool`, *optional*, defaults to `False`):237            Whether or not to force to (re-)download the configuration files and override the cached versions if they238            exist.239        resume_download (`bool`, *optional*, defaults to `False`):240            Whether or not to delete incompletely received file. Attempts to resume the download if such a file exists.241        proxies (`Dict[str, str]`, *optional*):242            A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',243            'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.244        token (`str` or *bool*, *optional*):245            The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated246            when running `huggingface-cli login` (stored in `~/.huggingface`).247        revision (`str`, *optional*, defaults to `"main"`):248            The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a249            git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any250            identifier allowed by git.251        local_files_only (`bool`, *optional*, defaults to `False`):252            If `True`, will only try to load the tokenizer configuration from local files.253        repo_type (`str`, *optional*):254            Specify the repo type (useful when downloading from a space for instance).255 256    <Tip>257 258    Passing `token=True` is required when you want to use a private model.259 260    </Tip>261 262    Returns:263        `str`: The path to the module inside the cache.264    """265    use_auth_token = deprecated_kwargs.pop("use_auth_token", None)266    if use_auth_token is not None:267        warnings.warn(268            "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers.", FutureWarning269        )270        if token is not None:271            raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")272        token = use_auth_token273 274    if is_offline_mode() and not local_files_only:275        logger.info("Offline mode: forcing local_files_only=True")276        local_files_only = True277 278    # Download and cache module_file from the repo `pretrained_model_name_or_path` of grab it if it's a local file.279    pretrained_model_name_or_path = str(pretrained_model_name_or_path)280    is_local = os.path.isdir(pretrained_model_name_or_path)281    if is_local:282        submodule = os.path.basename(pretrained_model_name_or_path)283    else:284        submodule = pretrained_model_name_or_path.replace("/", os.path.sep)285        cached_module = try_to_load_from_cache(286            pretrained_model_name_or_path, module_file, cache_dir=cache_dir, revision=_commit_hash, repo_type=repo_type287        )288 289    new_files = []290    try:291        # Load from URL or cache if already cached292        resolved_module_file = cached_file(293            pretrained_model_name_or_path,294            module_file,295            cache_dir=cache_dir,296            force_download=force_download,297            proxies=proxies,298            resume_download=resume_download,299            local_files_only=local_files_only,300            token=token,301            revision=revision,302            repo_type=repo_type,303            _commit_hash=_commit_hash,304        )305        if not is_local and cached_module != resolved_module_file:306            new_files.append(module_file)307 308    except EnvironmentError:309        logger.error(f"Could not locate the {module_file} inside {pretrained_model_name_or_path}.")310        raise311 312    # Check we have all the requirements in our environment313    modules_needed = check_imports(resolved_module_file)314 315    # Now we move the module inside our cached dynamic modules.316    full_submodule = TRANSFORMERS_DYNAMIC_MODULE_NAME + os.path.sep + submodule317    create_dynamic_module(full_submodule)318    submodule_path = Path(HF_MODULES_CACHE) / full_submodule319    if submodule == os.path.basename(pretrained_model_name_or_path):320        # We copy local files to avoid putting too many folders in sys.path. This copy is done when the file is new or321        # has changed since last copy.322        if not (submodule_path / module_file).exists() or not filecmp.cmp(323            resolved_module_file, str(submodule_path / module_file)324        ):325            shutil.copy(resolved_module_file, submodule_path / module_file)326            importlib.invalidate_caches()327        for module_needed in modules_needed:328            module_needed = f"{module_needed}.py"329            module_needed_file = os.path.join(pretrained_model_name_or_path, module_needed)330            if not (submodule_path / module_needed).exists() or not filecmp.cmp(331                module_needed_file, str(submodule_path / module_needed)332            ):333                shutil.copy(module_needed_file, submodule_path / module_needed)334                importlib.invalidate_caches()335    else:336        # Get the commit hash337        commit_hash = extract_commit_hash(resolved_module_file, _commit_hash)338 339        # The module file will end up being placed in a subfolder with the git hash of the repo. This way we get the340        # benefit of versioning.341        submodule_path = submodule_path / commit_hash342        full_submodule = full_submodule + os.path.sep + commit_hash343        create_dynamic_module(full_submodule)344 345        if not (submodule_path / module_file).exists():346            shutil.copy(resolved_module_file, submodule_path / module_file)347            importlib.invalidate_caches()348        # Make sure we also have every file with relative349        for module_needed in modules_needed:350            if not (submodule_path / f"{module_needed}.py").exists():351                get_cached_module_file(352                    pretrained_model_name_or_path,353                    f"{module_needed}.py",354                    cache_dir=cache_dir,355                    force_download=force_download,356                    resume_download=resume_download,357                    proxies=proxies,358                    token=token,359                    revision=revision,360                    local_files_only=local_files_only,361                    _commit_hash=commit_hash,362                )363                new_files.append(f"{module_needed}.py")364 365    if len(new_files) > 0 and revision is None:366        new_files = "\n".join([f"- {f}" for f in new_files])367        repo_type_str = "" if repo_type is None else f"{repo_type}s/"368        url = f"https://huggingface.co/{repo_type_str}{pretrained_model_name_or_path}"369        logger.warning(370            f"A new version of the following files was downloaded from {url}:\n{new_files}"371            "\n. Make sure to double-check they do not contain any added malicious code. To avoid downloading new "372            "versions of the code file, you can pin a revision."373        )374 375    return os.path.join(full_submodule, module_file)376 377 378def get_class_from_dynamic_module(379    class_reference: str,380    pretrained_model_name_or_path: Union[str, os.PathLike],381    cache_dir: Optional[Union[str, os.PathLike]] = None,382    force_download: bool = False,383    resume_download: bool = False,384    proxies: Optional[Dict[str, str]] = None,385    token: Optional[Union[bool, str]] = None,386    revision: Optional[str] = None,387    local_files_only: bool = False,388    repo_type: Optional[str] = None,389    code_revision: Optional[str] = None,390    **kwargs,391) -> typing.Type:392    """393    Extracts a class from a module file, present in the local folder or repository of a model.394 395    <Tip warning={true}>396 397    Calling this function will execute the code in the module file found locally or downloaded from the Hub. It should398    therefore only be called on trusted repos.399 400    </Tip>401 402    Args:403        class_reference (`str`):404            The full name of the class to load, including its module and optionally its repo.405        pretrained_model_name_or_path (`str` or `os.PathLike`):406            This can be either:407 408            - a string, the *model id* of a pretrained model configuration hosted inside a model repo on409              huggingface.co. Valid model ids can be located at the root-level, like `bert-base-uncased`, or namespaced410              under a user or organization name, like `dbmdz/bert-base-german-cased`.411            - a path to a *directory* containing a configuration file saved using the412              [`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.413 414            This is used when `class_reference` does not specify another repo.415        module_file (`str`):416            The name of the module file containing the class to look for.417        class_name (`str`):418            The name of the class to import in the module.419        cache_dir (`str` or `os.PathLike`, *optional*):420            Path to a directory in which a downloaded pretrained model configuration should be cached if the standard421            cache should not be used.422        force_download (`bool`, *optional*, defaults to `False`):423            Whether or not to force to (re-)download the configuration files and override the cached versions if they424            exist.425        resume_download (`bool`, *optional*, defaults to `False`):426            Whether or not to delete incompletely received file. Attempts to resume the download if such a file exists.427        proxies (`Dict[str, str]`, *optional*):428            A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',429            'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.430        token (`str` or `bool`, *optional*):431            The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated432            when running `huggingface-cli login` (stored in `~/.huggingface`).433        revision (`str`, *optional*, defaults to `"main"`):434            The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a435            git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any436            identifier allowed by git.437        local_files_only (`bool`, *optional*, defaults to `False`):438            If `True`, will only try to load the tokenizer configuration from local files.439        repo_type (`str`, *optional*):440            Specify the repo type (useful when downloading from a space for instance).441        code_revision (`str`, *optional*, defaults to `"main"`):442            The specific revision to use for the code on the Hub, if the code leaves in a different repository than the443            rest of the model. It can be a branch name, a tag name, or a commit id, since we use a git-based system for444            storing models and other artifacts on huggingface.co, so `revision` can be any identifier allowed by git.445 446    <Tip>447 448    Passing `token=True` is required when you want to use a private model.449 450    </Tip>451 452    Returns:453        `typing.Type`: The class, dynamically imported from the module.454 455    Examples:456 457    ```python458    # Download module `modeling.py` from huggingface.co and cache then extract the class `MyBertModel` from this459    # module.460    cls = get_class_from_dynamic_module("modeling.MyBertModel", "sgugger/my-bert-model")461 462    # Download module `modeling.py` from a given repo and cache then extract the class `MyBertModel` from this463    # module.464    cls = get_class_from_dynamic_module("sgugger/my-bert-model--modeling.MyBertModel", "sgugger/another-bert-model")465    ```"""466    use_auth_token = kwargs.pop("use_auth_token", None)467    if use_auth_token is not None:468        warnings.warn(469            "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers.", FutureWarning470        )471        if token is not None:472            raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")473        token = use_auth_token474 475    # Catch the name of the repo if it's specified in `class_reference`476    if "--" in class_reference:477        repo_id, class_reference = class_reference.split("--")478    else:479        repo_id = pretrained_model_name_or_path480    module_file, class_name = class_reference.split(".")481 482    if code_revision is None and pretrained_model_name_or_path == repo_id:483        code_revision = revision484    # And lastly we get the class inside our newly created module485    final_module = get_cached_module_file(486        repo_id,487        module_file + ".py",488        cache_dir=cache_dir,489        force_download=force_download,490        resume_download=resume_download,491        proxies=proxies,492        token=token,493        revision=code_revision,494        local_files_only=local_files_only,495        repo_type=repo_type,496    )497    return get_class_in_module(class_name, final_module.replace(".py", ""))498 499 500def custom_object_save(obj: Any, folder: Union[str, os.PathLike], config: Optional[Dict] = None) -> List[str]:501    """502    Save the modeling files corresponding to a custom model/configuration/tokenizer etc. in a given folder. Optionally503    adds the proper fields in a config.504 505    Args:506        obj (`Any`): The object for which to save the module files.507        folder (`str` or `os.PathLike`): The folder where to save.508        config (`PretrainedConfig` or dictionary, `optional`):509            A config in which to register the auto_map corresponding to this custom object.510 511    Returns:512        `List[str]`: The list of files saved.513    """514    if obj.__module__ == "__main__":515        logger.warning(516            f"We can't save the code defining {obj} in {folder} as it's been defined in __main__. You should put "517            "this code in a separate module so we can include it in the saved folder and make it easier to share via "518            "the Hub."519        )520        return521 522    def _set_auto_map_in_config(_config):523        module_name = obj.__class__.__module__524        last_module = module_name.split(".")[-1]525        full_name = f"{last_module}.{obj.__class__.__name__}"526        # Special handling for tokenizers527        if "Tokenizer" in full_name:528            slow_tokenizer_class = None529            fast_tokenizer_class = None530            if obj.__class__.__name__.endswith("Fast"):531                # Fast tokenizer: we have the fast tokenizer class and we may have the slow one has an attribute.532                fast_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"533                if getattr(obj, "slow_tokenizer_class", None) is not None:534                    slow_tokenizer = getattr(obj, "slow_tokenizer_class")535                    slow_tok_module_name = slow_tokenizer.__module__536                    last_slow_tok_module = slow_tok_module_name.split(".")[-1]537                    slow_tokenizer_class = f"{last_slow_tok_module}.{slow_tokenizer.__name__}"538            else:539                # Slow tokenizer: no way to have the fast class540                slow_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"541 542            full_name = (slow_tokenizer_class, fast_tokenizer_class)543 544        if isinstance(_config, dict):545            auto_map = _config.get("auto_map", {})546            auto_map[obj._auto_class] = full_name547            _config["auto_map"] = auto_map548        elif getattr(_config, "auto_map", None) is not None:549            _config.auto_map[obj._auto_class] = full_name550        else:551            _config.auto_map = {obj._auto_class: full_name}552 553    # Add object class to the config auto_map554    if isinstance(config, (list, tuple)):555        for cfg in config:556            _set_auto_map_in_config(cfg)557    elif config is not None:558        _set_auto_map_in_config(config)559 560    result = []561    # Copy module file to the output folder.562    object_file = sys.modules[obj.__module__].__file__563    dest_file = Path(folder) / (Path(object_file).name)564    shutil.copy(object_file, dest_file)565    result.append(dest_file)566 567    # Gather all relative imports recursively and make sure they are copied as well.568    for needed_file in get_relative_import_files(object_file):569        dest_file = Path(folder) / (Path(needed_file).name)570        shutil.copy(needed_file, dest_file)571        result.append(dest_file)572 573    return result574 575 576def _raise_timeout_error(signum, frame):577    raise ValueError(578        "Loading this model requires you to execute custom code contained in the model repository on your local"579        "machine. Please set the option `trust_remote_code=True` to permit loading of this model."580    )581 582 583TIME_OUT_REMOTE_CODE = 15584 585 586def resolve_trust_remote_code(trust_remote_code, model_name, has_local_code, has_remote_code):587    if trust_remote_code is None:588        if has_local_code:589            trust_remote_code = False590        elif has_remote_code and TIME_OUT_REMOTE_CODE > 0:591            try:592                signal.signal(signal.SIGALRM, _raise_timeout_error)593                signal.alarm(TIME_OUT_REMOTE_CODE)594                while trust_remote_code is None:595                    answer = input(596                        f"The repository for {model_name} contains custom code which must be executed to correctly"597                        f"load the model. You can inspect the repository content at https://hf.co/{model_name}.\n"598                        f"You can avoid this prompt in future by passing the argument `trust_remote_code=True`.\n\n"599                        f"Do you wish to run the custom code? [y/N] "600                    )601                    if answer.lower() in ["yes", "y", "1"]:602                        trust_remote_code = True603                    elif answer.lower() in ["no", "n", "0", ""]:604                        trust_remote_code = False605                signal.alarm(0)606            except Exception:607                # OS which does not support signal.SIGALRM608                raise ValueError(609                    f"The repository for {model_name} contains custom code which must be executed to correctly"610                    f"load the model. You can inspect the repository content at https://hf.co/{model_name}.\n"611                    f"Please pass the argument `trust_remote_code=True` to allow custom code to be run."612                )613        elif has_remote_code:614            # For the CI which puts the timeout at 0615            _raise_timeout_error(None, None)616 617    if has_remote_code and not has_local_code and not trust_remote_code:618        raise ValueError(619            f"Loading {model_name} requires you to execute the configuration file in that"620            " repo on your local machine. Make sure you have read the code there to avoid malicious use, then"621            " set the option `trust_remote_code=True` to remove this error."622        )623 624    return trust_remote_code625