# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License" # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import builtins import functools import importlib.util import os import shutil import site import sys from contextlib import contextmanager from typing import Optional, Tuple, Type, Union import pip from paddlenlp.utils.log import logger _original_import = builtins.__import__ _imported_modules = {} _paddlenlp_ops_updated = False _original_attributes = {} pybind_ops_list = [ "update_inputs_v2", "save_output", "set_preids_token_penalty_multi_scores", "rebuild_padding_v2", "append_attention", "save_output_dygraph", "per_token_group_quant", "per_tensor_quant_fp8", ] def custom_import(name, *args, **kwargs): global _paddlenlp_ops_updated, _imported_modules, _original_attributes global pybind_ops_list if _paddlenlp_ops_updated: if name in _imported_modules: return _imported_modules[name] module = _original_import(name, *args, **kwargs) if not _paddlenlp_ops_updated and os.getenv("DYNAMIC_INFERENCE_MODE", "1").lower() in [ "1", "true", "t", "yes", "y", ]: if name == "paddlenlp_ops": # logger.debug("Using Pybind paddlenlp_ops!") if name not in _original_attributes: bak_dict = {} for ops_name in pybind_ops_list: bak_dict[ops_name] = getattr(module, ops_name, None) _original_attributes[name] = bak_dict for ops_name in pybind_ops_list: pybind_ops_name = f"f_{ops_name}" if hasattr(module, pybind_ops_name): setattr(module, ops_name, getattr(module, pybind_ops_name)) _paddlenlp_ops_updated = True _imported_modules[name] = module return module @contextmanager def dynamic_graph_pybind_context(): global _original_import, _paddlenlp_ops_updated original_import = builtins.__import__ try: builtins.__import__ = custom_import yield finally: builtins.__import__ = original_import if "paddlenlp_ops" in _original_attributes: paddlenlp_ops_module = sys.modules.get("paddlenlp_ops") if paddlenlp_ops_module: for attr, value in _original_attributes["paddlenlp_ops"].items(): setattr(paddlenlp_ops_module, attr, value) _paddlenlp_ops_updated = False def auto_dynamic_graph_pybind(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): with dynamic_graph_pybind_context(): return func(self, *args, **kwargs) return wrapper # TODO: This doesn't work for all packages (`bs4`, `faiss`, etc.) Talk to Sylvain to see how to do with it better. def _is_package_available(pkg_name: str, return_version: bool = False) -> Union[Tuple[bool, str], bool]: # Check if the package spec exists and grab its version to avoid importing a local directory package_exists = importlib.util.find_spec(pkg_name) is not None package_version = "N/A" if package_exists: try: # Primary method to get the package version package_version = importlib.metadata.version(pkg_name) except importlib.metadata.PackageNotFoundError: # Fallback method: Only for "torch" and versions containing "dev" if pkg_name == "torch": try: package = importlib.import_module(pkg_name) temp_version = getattr(package, "__version__", "N/A") # Check if the version contains "dev" if "dev" in temp_version: package_version = temp_version package_exists = True else: package_exists = False except ImportError: # If the package can't be imported, it's not available package_exists = False else: # For packages other than "torch", don't attempt the fallback and set as not available package_exists = False if return_version: return package_exists, package_version else: return package_exists _g2p_en_available = _is_package_available("g2p_en") _sentencepiece_available = _is_package_available("sentencepiece") _sklearn_available = importlib.util.find_spec("sklearn") is not None if _sklearn_available: try: importlib.metadata.version("scikit-learn") except importlib.metadata.PackageNotFoundError: _sklearn_available = False # TODO: This doesn't work for all packages (`bs4`, `faiss`, etc.) Talk to Sylvain to see how to do with it better. def _is_package_available(pkg_name: str, return_version: bool = False) -> Union[Tuple[bool, str], bool]: # Check if the package spec exists and grab its version to avoid importing a local directory package_exists = importlib.util.find_spec(pkg_name) is not None package_version = "N/A" if package_exists: try: # Primary method to get the package version package_version = importlib.metadata.version(pkg_name) except importlib.metadata.PackageNotFoundError: # Fallback method: Only for "torch" and versions containing "dev" if pkg_name == "torch": try: package = importlib.import_module(pkg_name) temp_version = getattr(package, "__version__", "N/A") # Check if the version contains "dev" if "dev" in temp_version: package_version = temp_version package_exists = True else: package_exists = False except ImportError: # If the package can't be imported, it's not available package_exists = False else: # For packages other than "torch", don't attempt the fallback and set as not available package_exists = False if return_version: return package_exists, package_version else: return package_exists _g2p_en_available = _is_package_available("g2p_en") _sentencepiece_available = _is_package_available("sentencepiece") _sklearn_available = importlib.util.find_spec("sklearn") is not None if _sklearn_available: try: importlib.metadata.version("scikit-learn") except importlib.metadata.PackageNotFoundError: _sklearn_available = False def is_datasets_available(): import importlib return importlib.util.find_spec("datasets") is not None def is_protobuf_available(): if importlib.util.find_spec("google") is None: return False return importlib.util.find_spec("google.protobuf") is not None def is_paddle_cuda_available() -> bool: if is_paddle_available(): import paddle return paddle.device.cuda.device_count() > 0 else: return False def is_g2p_en_available(): return _g2p_en_available def is_sentencepiece_available(): return _sentencepiece_available def is_paddle_available() -> bool: """check if `torch` package is installed Returns: bool: if `torch` is available """ return is_package_available("paddle") def is_tiktoken_available(): return importlib.util.find_spec("tiktoken") is not None def is_psutil_available(): return importlib.util.find_spec("psutil") is not None def is_torch_available() -> bool: """check if `torch` package is installed Returns: bool: if `torch` is available """ return is_package_available("torch") def is_package_available(package_name: str) -> bool: """check if the package is available Args: package_name (str): the installed package name Returns: bool: the existence of installed package """ package_spec = importlib.util.find_spec(package_name) return package_spec is not None and package_spec.has_location def is_fast_tokenizer_available() -> bool: """check if `fast_tokenizer` ia available Returns: bool: if `fast_tokenizer` is available """ return is_package_available("fast_tokenizer") def is_tokenizers_available() -> bool: """check if `tokenizers` ia available Returns: bool: if `tokenizers` is available """ return is_package_available("tokenizers") def is_paddlenlp_ops_available() -> bool: """check if `paddlenlp_ops` ia available Returns: bool: if `paddlenlp_ops` is available """ return is_package_available("paddlenlp_ops") def is_transformers_available() -> bool: """check if `transformers` package is installed Returns: bool: if `transformers` is available """ return is_package_available("transformers") def install_package( package_name: str, version: Optional[str] = None, module_name: Optional[str] = None, cache_dir: Optional[str] = None, ): """install the specific version of package Args: package_name (str): the name of package version (str): the version of package module_name (str): the imported name of package cache_dir (str): cache dir """ module_name = module_name or package_name # 1. remove the existing version of package uninstall_package(package_name, module_name) # 2. install the package if version: package_name += f"=={version}" arguments = ["install"] if cache_dir: arguments += ["-t", cache_dir] sys.path.insert(0, cache_dir) # 3. load the pypi mirror to speedup of installing packages mirror_key = "PYPI_MIRROR" mirror_source = os.environ.get(mirror_key, None) if mirror_source is not None: logger.info(f"loading <{mirror_source}> from as the final mirror source to install package.") arguments += ["-i", mirror_source] arguments += [package_name] pip.main(arguments) # 4. add site-package to the top of package for site_package_dir in site.getsitepackages(): sys.path.insert(0, site_package_dir) def uninstall_package(package_name: str, module_name: Optional[str] = None): """uninstall the package from site-packages. To remove the cache of source package module & class & method, it should: 1. remove the source files of packages under the `site-packages` dir. 2. remove the cache under the `locals()` 3. remove the cache under the `sys.modules` Args: package_name (str): the name of package """ module_name = module_name or package_name for site_package_dir in site.getsitepackages(): if os.path.exists(site_package_dir): for file in os.listdir(site_package_dir): package_dir = os.path.join(site_package_dir, file) if file.startswith(package_name) and os.path.isdir(package_dir): shutil.rmtree(package_dir) for site_package_dir in site.getsitepackages(): while sys.path[0] == site_package_dir: sys.path.pop(0) for key in list(locals().keys()): if module_name in key: del locals()[key] for key in list(sys.modules.keys()): if module_name in key: del sys.modules[key] def import_module(module_name: str) -> Optional[Type]: """import module base on the model Args: module_name (str): the name of target module """ # 1. prepare the name assert "." in module_name, "`.` must be in the module_name" index = module_name.rindex(".") module = module_name[:index] target_module_name = module_name[index + 1 :] # 2. get the target module name try: module = importlib.import_module(module) target_module = getattr(module, target_module_name, None) return target_module except ModuleNotFoundError: return None