Load all configured plugins. Return a list of all the loaded plugins from the config file. The second return value is a snapshot of versions/hashes of loaded user plugins (for cache validation).
(
options: Options, errors: Errors, stdout: TextIO
)
| 633 | |
| 634 | |
| 635 | def load_plugins_from_config( |
| 636 | options: Options, errors: Errors, stdout: TextIO |
| 637 | ) -> tuple[list[Plugin], dict[str, str]]: |
| 638 | """Load all configured plugins. |
| 639 | |
| 640 | Return a list of all the loaded plugins from the config file. |
| 641 | The second return value is a snapshot of versions/hashes of loaded user |
| 642 | plugins (for cache validation). |
| 643 | """ |
| 644 | import importlib |
| 645 | |
| 646 | snapshot: dict[str, str] = {} |
| 647 | |
| 648 | if not options.config_file: |
| 649 | return [], snapshot |
| 650 | |
| 651 | line = find_config_file_line_number(options.config_file, "mypy", "plugins") |
| 652 | if line == -1: |
| 653 | line = 1 # We need to pick some line number that doesn't look too confusing |
| 654 | |
| 655 | def plugin_error(message: str) -> NoReturn: |
| 656 | errors.report(line, 0, message) |
| 657 | errors.raise_error(use_stdout=False) |
| 658 | |
| 659 | custom_plugins: list[Plugin] = [] |
| 660 | errors.set_file(options.config_file, None, options) |
| 661 | for plugin_path in options.plugins: |
| 662 | func_name = "plugin" |
| 663 | plugin_dir: str | None = None |
| 664 | if ":" in os.path.basename(plugin_path): |
| 665 | plugin_path, func_name = plugin_path.rsplit(":", 1) |
| 666 | if plugin_path.endswith(".py"): |
| 667 | # Plugin paths can be relative to the config file location. |
| 668 | plugin_path = os_path_join(os.path.dirname(options.config_file), plugin_path) |
| 669 | if not os.path.isfile(plugin_path): |
| 670 | plugin_error(f'Can\'t find plugin "{plugin_path}"') |
| 671 | # Use an absolute path to avoid populating the cache entry |
| 672 | # for 'tmp' during tests, since it will be different in |
| 673 | # different tests. |
| 674 | plugin_dir = os.path.abspath(os.path.dirname(plugin_path)) |
| 675 | fnam = os.path.basename(plugin_path) |
| 676 | module_name = fnam[:-3] |
| 677 | sys.path.insert(0, plugin_dir) |
| 678 | elif re.search(r"[\\/]", plugin_path): |
| 679 | fnam = os.path.basename(plugin_path) |
| 680 | plugin_error(f'Plugin "{fnam}" does not have a .py extension') |
| 681 | else: |
| 682 | module_name = plugin_path |
| 683 | |
| 684 | try: |
| 685 | module = importlib.import_module(module_name) |
| 686 | except Exception as exc: |
| 687 | plugin_error(f'Error importing plugin "{plugin_path}": {exc}') |
| 688 | finally: |
| 689 | if plugin_dir is not None: |
| 690 | assert sys.path[0] == plugin_dir |
| 691 | del sys.path[0] |
| 692 |
no test coverage detected
searching dependent graphs…