diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 629dacf0..7dc28158 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -11,6 +11,7 @@ import contextlib import doctest import functools +import importlib import os import re import string @@ -744,8 +745,10 @@ def __init__(self, tree, filename='(none)', builtins=None, self.withDoctest = withDoctest self.exceptHandlers = [()] self.root = tree - self.scopeStack = [] + + self.load_plugins() + try: scope_tp = Checker._ast_node_scope[type(tree)] except KeyError: @@ -765,6 +768,29 @@ def __init__(self, tree, filename='(none)', builtins=None, stacklevel=2, ) + def load_plugins(self) -> None: + """Load all plugins""" + plugins_directory = os.path.normpath(os.path.join(os.path.dirname( + __file__), 'plugins')) + if not os.path.exists(plugins_directory): + return + for filename in os.listdir(plugins_directory): + if filename.startswith('_'): + continue + path = os.path.join(plugins_directory, filename) + base, extension = os.path.splitext(filename) + if extension != '.py' : + continue + try: + module_name = f"plugins.{base}" + plugin = importlib.import_module(module_name) + importlib.reload(plugin) + # print(f"Loaded plugin: {module_name}") + if hasattr(plugin, "register"): + plugin.register(self) + except Exception as e: + print(f"Can not import {module_name}: {e}") + def deferFunction(self, callable): """ Schedule a function handler to be called just before completion. diff --git a/pyflakes/plugins/__init__.py b/pyflakes/plugins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyflakes/plugins/leo_plugin.py b/pyflakes/plugins/leo_plugin.py new file mode 100644 index 00000000..0c45e486 --- /dev/null +++ b/pyflakes/plugins/leo_plugin.py @@ -0,0 +1,45 @@ +#@+leo-ver=5-thin +#@+node:ekr.20251125174431.1: * @file plugins/leo_plugin.py +"""A plugin for pyflakes that improves testing of ast.ATTRIBUTE nodes.""" + +import ast +import os + +#@+others +#@+node:ekr.20251126064813.1: ** leo_plugin: funcToMethod +def funcToMethod(f: Callable, theClass: object, name: str = None) -> None: + """ + From the Python Cookbook... + + The following method allows you to add a function as a method of + any class. That is, it converts the function to a method of the + class. The method just added is available instantly to all + existing instances of the class, and to all instances created in + the future. + + The function's first argument should be self. + + The newly created method has the same name as the function unless + the optional name argument is supplied, in which case that name is + used as the method name. + """ + setattr(theClass, name or f.__name__, f) + +#@+node:ekr.20251126060205.1: ** leo_plugin: patched_ATTRIBUTE +def patched_ATTRIBUTE(self, node) -> None: + if isinstance(node.ctx, ast.Load): + if isinstance(node.value, ast.Name): + if 1: ### Don't put this in production code! + print(f"patched_ATTRIBUTE: load {node.value.id}.{node.attr}") + self.handleChildren(node) +#@+node:ekr.20251126054330.1: ** leo_plugin: register +def register(pyflakes) -> None: + """Register the leo_plugin plugin.""" + if 0: + path, extension = os.path.splitext(__file__) + print(f"V6: {os.path.basename(path)}.register: pyflakes: {pyflakes!r}") + + # Patch pyflakes.ATTRIBUTE. + funcToMethod(patched_ATTRIBUTE, pyflakes.__class__, 'ATTRIBUTE') +#@-others +#@-leo