CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
dynamic_module_utils.py844 linesDownload Raw Back to transformers
1# Copyright 2021 The HuggingFace Inc. team.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 at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14"""Utilities to dynamically load objects from the Hub."""15 16import ast17import filecmp18import hashlib19import importlib20import importlib.metadata21import importlib.util22import keyword23import os24import re25import shutil26import signal27import sys28import threading29import warnings30from pathlib import Path31from types import ModuleType32from typing import Any, Optional, Union33 34from huggingface_hub import try_to_load_from_cache35from packaging import version36 37from .utils import (38    HF_MODULES_CACHE,39    TRANSFORMERS_DYNAMIC_MODULE_NAME,40    cached_file,41    extract_commit_hash,42    is_offline_mode,43    logging,44)45from .utils.import_utils import VersionComparison, split_package_version46 47 48logger = logging.get_logger(__name__)  # pylint: disable=invalid-name49 50 51def _sanitize_module_name(name: str) -> str:52    r"""53    Tries to sanitize a module name so that it can be used as a Python module.54 55    The following transformations are applied:56 57    1. Replace `.` in module names with `_dot_`.58    2. Replace `-` in module names with `_hyphen_`.59    3. If the module name starts with a digit, prepend it with `_`.60    4. Warn if the sanitized name is a Python reserved keyword or not a valid identifier.61 62    If the input name is already a valid identifier, it is returned unchanged.63    """64    # We not replacing `\W` characters with `_` to avoid collisions. Because `_` is a very common65    # separator used in module names, replacing `\W` with `_` would create too many collisions.66    # Once a module is imported, it is cached in `sys.modules` and the second import would return67    # the first module, which might not be the expected behavior if name collisions happen.68    new_name = name.replace(".", "_dot_").replace("-", "_hyphen_")69    if new_name and new_name[0].isdigit():70        new_name = f"_{new_name}"71    if keyword.iskeyword(new_name):72        logger.warning(73            f"The module name {new_name} (originally {name}) is a reserved keyword in Python. "74            "Please rename the original module to avoid import issues."75        )76    elif not new_name.isidentifier():77        logger.warning(78            f"The module name {new_name} (originally {name}) is not a valid Python identifier. "79            "Please rename the original module to avoid import issues."80        )81    return new_name82 83 84_HF_REMOTE_CODE_LOCK = threading.Lock()85 86 87def init_hf_modules():88    """89    Creates the cache directory for modules with an init, and adds it to the Python path.90    """91    # This function has already been executed if HF_MODULES_CACHE already is in the Python path.92    if HF_MODULES_CACHE in sys.path:93        return94 95    sys.path.append(HF_MODULES_CACHE)96    os.makedirs(HF_MODULES_CACHE, exist_ok=True)97    init_path = Path(HF_MODULES_CACHE) / "__init__.py"98    if not init_path.exists():99        init_path.touch()100        importlib.invalidate_caches()101 102 103def create_dynamic_module(name: Union[str, os.PathLike]) -> None:104    """105    Creates a dynamic module in the cache directory for modules.106 107    Args:108        name (`str` or `os.PathLike`):109            The name of the dynamic module to create.110    """111    init_hf_modules()112    dynamic_module_path = (Path(HF_MODULES_CACHE) / name).resolve()113    # If the parent module does not exist yet, recursively create it.114    if not dynamic_module_path.parent.exists():115        create_dynamic_module(dynamic_module_path.parent)116    os.makedirs(dynamic_module_path, exist_ok=True)117    init_path = dynamic_module_path / "__init__.py"118    if not init_path.exists():119        init_path.touch()120        # It is extremely important to invalidate the cache when we change stuff in those modules, or users end up121        # with errors about module that do not exist. Same for all other `invalidate_caches` in this file.122        importlib.invalidate_caches()123 124 125def get_relative_imports(module_file: Union[str, os.PathLike]) -> list[str]:126    """127    Get the list of modules that are relatively imported in a module file.128 129    Args:130        module_file (`str` or `os.PathLike`): The module file to inspect.131 132    Returns:133        `list[str]`: The list of relative imports in the module.134    """135    with open(module_file, encoding="utf-8") as f:136        content = f.read()137 138    # Imports of the form `import .xxx`139    relative_imports = re.findall(r"^\s*import\s+\.(\S+)\s*$", content, flags=re.MULTILINE)140    # Imports of the form `from .xxx import yyy`141    relative_imports += re.findall(r"^\s*from\s+\.(\S+)\s+import", content, flags=re.MULTILINE)142    # Unique-ify143    return list(set(relative_imports))144 145 146def get_relative_import_files(module_file: Union[str, os.PathLike]) -> list[str]:147    """148    Get the list of all files that are needed for a given module. Note that this function recurses through the relative149    imports (if a imports b and b imports c, it will return module files for b and c).150 151    Args:152        module_file (`str` or `os.PathLike`): The module file to inspect.153 154    Returns:155        `list[str]`: The list of all relative imports a given module needs (recursively), which will give us the list156        of module files a given module needs.157    """158    no_change = False159    files_to_check = [module_file]160    all_relative_imports = []161 162    # Let's recurse through all relative imports163    while not no_change:164        new_imports = []165        for f in files_to_check:166            new_imports.extend(get_relative_imports(f))167 168        module_path = Path(module_file).parent169        new_import_files = [f"{str(module_path / m)}.py" for m in new_imports]170        files_to_check = [f for f in new_import_files if f not in all_relative_imports]171 172        no_change = len(files_to_check) == 0173        all_relative_imports.extend(files_to_check)174 175    return all_relative_imports176 177 178def get_imports(filename: Union[str, os.PathLike]) -> list[str]:179    """180    Extracts all the libraries (not relative imports this time) that are imported in a file.181 182    Args:183        filename (`str` or `os.PathLike`): The module file to inspect.184 185    Returns:186        `list[str]`: The list of all packages required to use the input module.187    """188    with open(filename, encoding="utf-8") as f:189        content = f.read()190    imported_modules = set()191 192    import transformers.utils193 194    def recursive_look_for_imports(node):195        if isinstance(node, ast.Try):196            return  # Don't recurse into Try blocks and ignore imports in them197        elif isinstance(node, ast.If):198            test = node.test199            for condition_node in ast.walk(test):200                if isinstance(condition_node, ast.Call):201                    check_function = getattr(condition_node.func, "id", "")202                    if (203                        check_function.endswith("available")204                        and check_function.startswith("is_flash_attn")205                        or hasattr(transformers.utils.import_utils, check_function)206                    ):207                        # Don't recurse into "if flash_attn_available()" or any "if library_available" blocks208                        # that appears in `transformers.utils.import_utils` and ignore imports in them209                        return210        elif isinstance(node, ast.Import):211            # Handle 'import x' statements212            for alias in node.names:213                top_module = alias.name.split(".")[0]214                if top_module:215                    imported_modules.add(top_module)216        elif isinstance(node, ast.ImportFrom):217            # Handle 'from x import y' statements, ignoring relative imports218            if node.level == 0 and node.module:219                top_module = node.module.split(".")[0]220                if top_module:221                    imported_modules.add(top_module)222 223        # Recursively visit all children224        for child in ast.iter_child_nodes(node):225            recursive_look_for_imports(child)226 227    tree = ast.parse(content)228    recursive_look_for_imports(tree)229 230    return sorted(imported_modules)231 232 233def check_imports(filename: Union[str, os.PathLike]) -> list[str]:234    """235    Check if the current Python environment contains all the libraries that are imported in a file. Will raise if a236    library is missing.237 238    Args:239        filename (`str` or `os.PathLike`): The module file to check.240 241    Returns:242        `list[str]`: The list of relative imports in the file.243    """244    imports = get_imports(filename)245    missing_packages = []246    for imp in imports:247        try:248            importlib.import_module(imp)249        except ImportError as exception:250            logger.warning(f"Encountered exception while importing {imp}: {exception}")251            # Some packages can fail with an ImportError because of a dependency issue.252            # This check avoids hiding such errors.253            # See https://github.com/huggingface/transformers/issues/33604254            if "No module named" in str(exception):255                missing_packages.append(imp)256            else:257                raise258 259    if len(missing_packages) > 0:260        raise ImportError(261            "This modeling file requires the following packages that were not found in your environment: "262            f"{', '.join(missing_packages)}. Run `pip install {' '.join(missing_packages)}`"263        )264 265    return get_relative_imports(filename)266 267 268def get_class_in_module(269    class_name: str,270    module_path: Union[str, os.PathLike],271    *,272    force_reload: bool = False,273) -> type:274    """275    Import a module on the cache directory for modules and extract a class from it.276 277    Args:278        class_name (`str`): The name of the class to import.279        module_path (`str` or `os.PathLike`): The path to the module to import.280        force_reload (`bool`, *optional*, defaults to `False`):281            Whether to reload the dynamic module from file if it already exists in `sys.modules`.282            Otherwise, the module is only reloaded if the file has changed.283 284    Returns:285        `typing.Type`: The class looked for.286    """287    name = os.path.normpath(module_path)288    name = name.removesuffix(".py")289    name = name.replace(os.path.sep, ".")290    module_file: Path = Path(HF_MODULES_CACHE) / module_path291    with _HF_REMOTE_CODE_LOCK:292        if force_reload:293            sys.modules.pop(name, None)294            importlib.invalidate_caches()295        cached_module: Optional[ModuleType] = sys.modules.get(name)296        module_spec = importlib.util.spec_from_file_location(name, location=module_file)297 298        # Hash the module file and all its relative imports to check if we need to reload it299        module_files: list[Path] = [module_file] + sorted(map(Path, get_relative_import_files(module_file)))300        module_hash: str = hashlib.sha256(b"".join(bytes(f) + f.read_bytes() for f in module_files)).hexdigest()301 302        module: ModuleType303        if cached_module is None:304            module = importlib.util.module_from_spec(module_spec)305            # insert it into sys.modules before any loading begins306            sys.modules[name] = module307        else:308            module = cached_module309        # reload in both cases, unless the module is already imported and the hash hits310        if getattr(module, "__transformers_module_hash__", "") != module_hash:311            module_spec.loader.exec_module(module)312            module.__transformers_module_hash__ = module_hash313        return getattr(module, class_name)314 315 316def get_cached_module_file(317    pretrained_model_name_or_path: Union[str, os.PathLike],318    module_file: str,319    cache_dir: Optional[Union[str, os.PathLike]] = None,320    force_download: bool = False,321    resume_download: Optional[bool] = None,322    proxies: Optional[dict[str, str]] = None,323    token: Optional[Union[bool, str]] = None,324    revision: Optional[str] = None,325    local_files_only: bool = False,326    repo_type: Optional[str] = None,327    _commit_hash: Optional[str] = None,328    **deprecated_kwargs,329) -> str:330    """331    Prepares Downloads a module from a local folder or a distant repo and returns its path inside the cached332    Transformers module.333 334    Args:335        pretrained_model_name_or_path (`str` or `os.PathLike`):336            This can be either:337 338            - a string, the *model id* of a pretrained model configuration hosted inside a model repo on339              huggingface.co.340            - a path to a *directory* containing a configuration file saved using the341              [`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.342 343        module_file (`str`):344            The name of the module file containing the class to look for.345        cache_dir (`str` or `os.PathLike`, *optional*):346            Path to a directory in which a downloaded pretrained model configuration should be cached if the standard347            cache should not be used.348        force_download (`bool`, *optional*, defaults to `False`):349            Whether or not to force to (re-)download the configuration files and override the cached versions if they350            exist.351        resume_download:352            Deprecated and ignored. All downloads are now resumed by default when possible.353            Will be removed in v5 of Transformers.354        proxies (`dict[str, str]`, *optional*):355            A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',356            'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.357        token (`str` or *bool*, *optional*):358            The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated359            when running `hf auth login` (stored in `~/.huggingface`).360        revision (`str`, *optional*, defaults to `"main"`):361            The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a362            git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any363            identifier allowed by git.364        local_files_only (`bool`, *optional*, defaults to `False`):365            If `True`, will only try to load the tokenizer configuration from local files.366        repo_type (`str`, *optional*):367            Specify the repo type (useful when downloading from a space for instance).368 369    <Tip>370 371    Passing `token=True` is required when you want to use a private model.372 373    </Tip>374 375    Returns:376        `str`: The path to the module inside the cache.377    """378    use_auth_token = deprecated_kwargs.pop("use_auth_token", None)379    if use_auth_token is not None:380        warnings.warn(381            "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",382            FutureWarning,383        )384        if token is not None:385            raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")386        token = use_auth_token387 388    if is_offline_mode() and not local_files_only:389        logger.info("Offline mode: forcing local_files_only=True")390        local_files_only = True391 392    # Download and cache module_file from the repo `pretrained_model_name_or_path` of grab it if it's a local file.393    pretrained_model_name_or_path = str(pretrained_model_name_or_path)394    is_local = os.path.isdir(pretrained_model_name_or_path)395    if is_local:396        submodule = _sanitize_module_name(os.path.basename(pretrained_model_name_or_path))397    else:398        submodule = os.path.sep.join(map(_sanitize_module_name, pretrained_model_name_or_path.split("/")))399        cached_module = try_to_load_from_cache(400            pretrained_model_name_or_path, module_file, cache_dir=cache_dir, revision=_commit_hash, repo_type=repo_type401        )402 403    new_files = []404    try:405        # Load from URL or cache if already cached406        resolved_module_file = cached_file(407            pretrained_model_name_or_path,408            module_file,409            cache_dir=cache_dir,410            force_download=force_download,411            proxies=proxies,412            resume_download=resume_download,413            local_files_only=local_files_only,414            token=token,415            revision=revision,416            repo_type=repo_type,417            _commit_hash=_commit_hash,418        )419        if not is_local and cached_module != resolved_module_file:420            new_files.append(module_file)421 422    except OSError:423        logger.info(f"Could not locate the {module_file} inside {pretrained_model_name_or_path}.")424        raise425 426    # Check we have all the requirements in our environment427    modules_needed = check_imports(resolved_module_file)428 429    # Now we move the module inside our cached dynamic modules.430    full_submodule = TRANSFORMERS_DYNAMIC_MODULE_NAME + os.path.sep + submodule431    create_dynamic_module(full_submodule)432    submodule_path = Path(HF_MODULES_CACHE) / full_submodule433    if submodule == _sanitize_module_name(os.path.basename(pretrained_model_name_or_path)):434        # We copy local files to avoid putting too many folders in sys.path. This copy is done when the file is new or435        # has changed since last copy.436        if not (submodule_path / module_file).exists() or not filecmp.cmp(437            resolved_module_file, str(submodule_path / module_file)438        ):439            (submodule_path / module_file).parent.mkdir(parents=True, exist_ok=True)440            shutil.copy(resolved_module_file, submodule_path / module_file)441            importlib.invalidate_caches()442        for module_needed in modules_needed:443            module_needed = Path(module_file).parent / f"{module_needed}.py"444            module_needed_file = os.path.join(pretrained_model_name_or_path, module_needed)445            if not (submodule_path / module_needed).exists() or not filecmp.cmp(446                module_needed_file, str(submodule_path / module_needed)447            ):448                shutil.copy(module_needed_file, submodule_path / module_needed)449                importlib.invalidate_caches()450    else:451        # Get the commit hash452        commit_hash = extract_commit_hash(resolved_module_file, _commit_hash)453 454        # The module file will end up being placed in a subfolder with the git hash of the repo. This way we get the455        # benefit of versioning.456        submodule_path = submodule_path / commit_hash457        full_submodule = full_submodule + os.path.sep + commit_hash458        full_submodule_module_file_path = os.path.join(full_submodule, module_file)459        create_dynamic_module(Path(full_submodule_module_file_path).parent)460 461        if not (submodule_path / module_file).exists():462            shutil.copy(resolved_module_file, submodule_path / module_file)463            importlib.invalidate_caches()464        # Make sure we also have every file with relative465        for module_needed in modules_needed:466            if not ((submodule_path / module_file).parent / f"{module_needed}.py").exists():467                get_cached_module_file(468                    pretrained_model_name_or_path,469                    f"{Path(module_file).parent / module_needed}.py",470                    cache_dir=cache_dir,471                    force_download=force_download,472                    resume_download=resume_download,473                    proxies=proxies,474                    token=token,475                    revision=revision,476                    local_files_only=local_files_only,477                    _commit_hash=commit_hash,478                )479                new_files.append(f"{module_needed}.py")480 481    if len(new_files) > 0 and revision is None:482        new_files = "\n".join([f"- {f}" for f in new_files])483        repo_type_str = "" if repo_type is None else f"{repo_type}s/"484        url = f"https://huggingface.co/{repo_type_str}{pretrained_model_name_or_path}"485        logger.warning(486            f"A new version of the following files was downloaded from {url}:\n{new_files}"487            "\n. Make sure to double-check they do not contain any added malicious code. To avoid downloading new "488            "versions of the code file, you can pin a revision."489        )490 491    return os.path.join(full_submodule, module_file)492 493 494def get_class_from_dynamic_module(495    class_reference: str,496    pretrained_model_name_or_path: Union[str, os.PathLike],497    cache_dir: Optional[Union[str, os.PathLike]] = None,498    force_download: bool = False,499    resume_download: Optional[bool] = None,500    proxies: Optional[dict[str, str]] = None,501    token: Optional[Union[bool, str]] = None,502    revision: Optional[str] = None,503    local_files_only: bool = False,504    repo_type: Optional[str] = None,505    code_revision: Optional[str] = None,506    **kwargs,507) -> type:508    """509    Extracts a class from a module file, present in the local folder or repository of a model.510 511    <Tip warning={true}>512 513    Calling this function will execute the code in the module file found locally or downloaded from the Hub. It should514    therefore only be called on trusted repos.515 516    </Tip>517 518 519 520    Args:521        class_reference (`str`):522            The full name of the class to load, including its module and optionally its repo.523        pretrained_model_name_or_path (`str` or `os.PathLike`):524            This can be either:525 526            - a string, the *model id* of a pretrained model configuration hosted inside a model repo on527              huggingface.co.528            - a path to a *directory* containing a configuration file saved using the529              [`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.530 531            This is used when `class_reference` does not specify another repo.532        module_file (`str`):533            The name of the module file containing the class to look for.534        class_name (`str`):535            The name of the class to import in the module.536        cache_dir (`str` or `os.PathLike`, *optional*):537            Path to a directory in which a downloaded pretrained model configuration should be cached if the standard538            cache should not be used.539        force_download (`bool`, *optional*, defaults to `False`):540            Whether or not to force to (re-)download the configuration files and override the cached versions if they541            exist.542        resume_download:543            Deprecated and ignored. All downloads are now resumed by default when possible.544            Will be removed in v5 of Transformers.545        proxies (`dict[str, str]`, *optional*):546            A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',547            'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.548        token (`str` or `bool`, *optional*):549            The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated550            when running `hf auth login` (stored in `~/.huggingface`).551        revision (`str`, *optional*, defaults to `"main"`):552            The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a553            git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any554            identifier allowed by git.555        local_files_only (`bool`, *optional*, defaults to `False`):556            If `True`, will only try to load the tokenizer configuration from local files.557        repo_type (`str`, *optional*):558            Specify the repo type (useful when downloading from a space for instance).559        code_revision (`str`, *optional*, defaults to `"main"`):560            The specific revision to use for the code on the Hub, if the code leaves in a different repository than the561            rest of the model. It can be a branch name, a tag name, or a commit id, since we use a git-based system for562            storing models and other artifacts on huggingface.co, so `revision` can be any identifier allowed by git.563 564    <Tip>565 566    Passing `token=True` is required when you want to use a private model.567 568    </Tip>569 570    Returns:571        `typing.Type`: The class, dynamically imported from the module.572 573    Examples:574 575    ```python576    # Download module `modeling.py` from huggingface.co and cache then extract the class `MyBertModel` from this577    # module.578    cls = get_class_from_dynamic_module("modeling.MyBertModel", "sgugger/my-bert-model")579 580    # Download module `modeling.py` from a given repo and cache then extract the class `MyBertModel` from this581    # module.582    cls = get_class_from_dynamic_module("sgugger/my-bert-model--modeling.MyBertModel", "sgugger/another-bert-model")583    ```"""584    use_auth_token = kwargs.pop("use_auth_token", None)585    if use_auth_token is not None:586        warnings.warn(587            "The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",588            FutureWarning,589        )590        if token is not None:591            raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")592        token = use_auth_token593 594    # Catch the name of the repo if it's specified in `class_reference`595    if "--" in class_reference:596        repo_id, class_reference = class_reference.split("--")597    else:598        repo_id = pretrained_model_name_or_path599    module_file, class_name = class_reference.split(".")600 601    if code_revision is None and pretrained_model_name_or_path == repo_id:602        code_revision = revision603    # And lastly we get the class inside our newly created module604    final_module = get_cached_module_file(605        repo_id,606        module_file + ".py",607        cache_dir=cache_dir,608        force_download=force_download,609        resume_download=resume_download,610        proxies=proxies,611        token=token,612        revision=code_revision,613        local_files_only=local_files_only,614        repo_type=repo_type,615    )616    return get_class_in_module(class_name, final_module, force_reload=force_download)617 618 619def custom_object_save(obj: Any, folder: Union[str, os.PathLike], config: Optional[dict] = None) -> list[str]:620    """621    Save the modeling files corresponding to a custom model/configuration/tokenizer etc. in a given folder. Optionally622    adds the proper fields in a config.623 624    Args:625        obj (`Any`): The object for which to save the module files.626        folder (`str` or `os.PathLike`): The folder where to save.627        config (`PretrainedConfig` or dictionary, `optional`):628            A config in which to register the auto_map corresponding to this custom object.629 630    Returns:631        `list[str]`: The list of files saved.632    """633    if obj.__module__ == "__main__":634        logger.warning(635            f"We can't save the code defining {obj} in {folder} as it's been defined in __main__. You should put "636            "this code in a separate module so we can include it in the saved folder and make it easier to share via "637            "the Hub."638        )639        return640 641    def _set_auto_map_in_config(_config):642        module_name = obj.__class__.__module__643        last_module = module_name.split(".")[-1]644        full_name = f"{last_module}.{obj.__class__.__name__}"645        # Special handling for tokenizers646        if "Tokenizer" in full_name:647            slow_tokenizer_class = None648            fast_tokenizer_class = None649            if obj.__class__.__name__.endswith("Fast"):650                # Fast tokenizer: we have the fast tokenizer class and we may have the slow one has an attribute.651                fast_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"652                if getattr(obj, "slow_tokenizer_class", None) is not None:653                    slow_tokenizer = getattr(obj, "slow_tokenizer_class")654                    slow_tok_module_name = slow_tokenizer.__module__655                    last_slow_tok_module = slow_tok_module_name.split(".")[-1]656                    slow_tokenizer_class = f"{last_slow_tok_module}.{slow_tokenizer.__name__}"657            else:658                # Slow tokenizer: no way to have the fast class659                slow_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"660 661            full_name = (slow_tokenizer_class, fast_tokenizer_class)662 663        if isinstance(_config, dict):664            auto_map = _config.get("auto_map", {})665            auto_map[obj._auto_class] = full_name666            _config["auto_map"] = auto_map667        elif getattr(_config, "auto_map", None) is not None:668            _config.auto_map[obj._auto_class] = full_name669        else:670            _config.auto_map = {obj._auto_class: full_name}671 672    # Add object class to the config auto_map673    if isinstance(config, (list, tuple)):674        for cfg in config:675            _set_auto_map_in_config(cfg)676    elif config is not None:677        _set_auto_map_in_config(config)678 679    result = []680    # Copy module file to the output folder.681    object_file = sys.modules[obj.__module__].__file__682    dest_file = Path(folder) / (Path(object_file).name)683    shutil.copy(object_file, dest_file)684    result.append(dest_file)685 686    # Gather all relative imports recursively and make sure they are copied as well.687    for needed_file in get_relative_import_files(object_file):688        dest_file = Path(folder) / (Path(needed_file).name)689        shutil.copy(needed_file, dest_file)690        result.append(dest_file)691 692    return result693 694 695def _raise_timeout_error(signum, frame):696    raise ValueError(697        "Loading this model requires you to execute custom code contained in the model repository on your local "698        "machine. Please set the option `trust_remote_code=True` to permit loading of this model."699    )700 701 702TIME_OUT_REMOTE_CODE = 15703 704 705def resolve_trust_remote_code(706    trust_remote_code, model_name, has_local_code, has_remote_code, error_message=None, upstream_repo=None707):708    """709    Resolves the `trust_remote_code` argument. If there is remote code to be loaded, the user must opt-in to loading710    it.711 712    Args:713        trust_remote_code (`bool` or `None`):714            User-defined `trust_remote_code` value.715        model_name (`str`):716            The name of the model repository in huggingface.co.717        has_local_code (`bool`):718            Whether the model has local code.719        has_remote_code (`bool`):720            Whether the model has remote code.721        error_message (`str`, *optional*):722            Custom error message to display if there is remote code to load and the user didn't opt-in. If unset, the error723            message will be regarding loading a model with custom code.724 725    Returns:726        The resolved `trust_remote_code` value.727    """728    if error_message is None:729        if upstream_repo is not None:730            error_message = (731                f"The repository {model_name} references custom code contained in {upstream_repo} which "732                f"must be executed to correctly load the model. You can inspect the repository "733                f"content at https://hf.co/{upstream_repo} .\n"734            )735        elif os.path.isdir(model_name):736            error_message = (737                f"The repository {model_name} contains custom code which must be executed "738                f"to correctly load the model. You can inspect the repository "739                f"content at {os.path.abspath(model_name)} .\n"740            )741        else:742            error_message = (743                f"The repository {model_name} contains custom code which must be executed "744                f"to correctly load the model. You can inspect the repository "745                f"content at https://hf.co/{model_name} .\n"746            )747 748    if trust_remote_code is None:749        if has_local_code:750            trust_remote_code = False751        elif has_remote_code and TIME_OUT_REMOTE_CODE > 0:752            prev_sig_handler = None753            try:754                prev_sig_handler = signal.signal(signal.SIGALRM, _raise_timeout_error)755                signal.alarm(TIME_OUT_REMOTE_CODE)756                while trust_remote_code is None:757                    answer = input(758                        f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"759                        f"You can avoid this prompt in future by passing the argument `trust_remote_code=True`.\n\n"760                        f"Do you wish to run the custom code? [y/N] "761                    )762                    if answer.lower() in ["yes", "y", "1"]:763                        trust_remote_code = True764                    elif answer.lower() in ["no", "n", "0", ""]:765                        trust_remote_code = False766                signal.alarm(0)767            except Exception:768                # OS which does not support signal.SIGALRM769                raise ValueError(770                    f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"771                    f"Please pass the argument `trust_remote_code=True` to allow custom code to be run."772                )773            finally:774                if prev_sig_handler is not None:775                    signal.signal(signal.SIGALRM, prev_sig_handler)776                    signal.alarm(0)777        elif has_remote_code:778            # For the CI which puts the timeout at 0779            _raise_timeout_error(None, None)780 781    if has_remote_code and not has_local_code and not trust_remote_code:782        raise ValueError(783            f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"784            f"Please pass the argument `trust_remote_code=True` to allow custom code to be run."785        )786 787    return trust_remote_code788 789 790def check_python_requirements(path_or_repo_id, requirements_file="requirements.txt", **kwargs):791    """792    Tries to locate `requirements_file` in a local folder or repo, and confirms that the environment has all the793    python dependencies installed.794 795    Args:796        path_or_repo_id (`str` or `os.PathLike`):797            This can be either:798            - a string, the *model id* of a model repo on huggingface.co.799            - a path to a *directory* potentially containing the file.800        kwargs (`dict[str, Any]`, *optional*):801            Additional arguments to pass to `cached_file`.802    """803    failed = []  # error messages regarding requirements804    try:805        requirements = cached_file(path_or_repo_id=path_or_repo_id, filename=requirements_file, **kwargs)806        with open(requirements, "r") as f:807            requirements = f.readlines()808 809        for requirement in requirements:810            requirement = requirement.strip()811            if not requirement or requirement.startswith("#"):  # skip empty lines and comments812                continue813 814            try:815                # e.g. "torch>2.6.0" -> "torch", ">", "2.6.0"816                package_name, delimiter, version_number = split_package_version(requirement)817            except ValueError:  # e.g. "torch", as opposed to "torch>2.6.0"818                package_name = requirement819                delimiter, version_number = None, None820 821            try:822                local_package_version = importlib.metadata.version(package_name)823            except importlib.metadata.PackageNotFoundError:824                failed.append(f"{requirement} (installed: None)")825                continue826 827            if delimiter is not None and version_number is not None:828                is_satisfied = VersionComparison.from_string(delimiter)(829                    version.parse(local_package_version), version.parse(version_number)830                )831            else:832                is_satisfied = True833 834            if not is_satisfied:835                failed.append(f"{requirement} (installed: {local_package_version})")836 837    except OSError:  # no requirements.txt838        pass839 840    if failed:841        raise ImportError(842            f"Missing requirements in your local environment for `{path_or_repo_id}`:\n" + "\n".join(failed)843        )844 
Aluode/PerceptionLabPortable · CoolFace