Aha re monkey patching

35 views
Skip to first unread message

Edward K. Ream

unread,
Nov 27, 2025, 6:20:15 AM (3 days ago) Nov 27
to leo-editor
Working directly in site-packages/pyflakes is turning out to be an excellent (if eccentric) strategy.  It's liberating to use live code without cloning or forking a repo.

Yesterday I found the last piece of the puzzle. Aha!  g.funcToMethod (a thin wrapper for setattr) can be applied to classes, not just class instances! 

This Aha changes everything! There is no need to create a plugin framework for pyflakes! Checker.__init__ doesn't need to change! Here's my latest experimental (Leonine!) script:

import ast
from pyflakes.api import check
from pyflakes.checker import Checker
<< define test >>
# Define an ATTRIBUTE function,
# to be converted to a Checker method.
@others 
g.funcToMethod(ATTRIBUTE, Checker)
check(test, filename='pyflakes_test.py')

Any changes to the ATTRIBUTE function take effect immediately, without calling importlib.reload. That's big. I live for these Ahas.

Edward

P.S. Here is the latest version of the ATTRIBUTE function. 

def ATTRIBUTE(self, node) -> None:
    if isinstance(node.value, ast.Name):
        g.trace(
            f"{node.ctx.__class__.__name__}"
            f"{node.value.id}.{node.attr}")
    self.handleChildren(node)


It replaces (part of) this assignment in checker.py:

# "expr" type nodes
BOOLOP = UNARYOP = SET = ATTRIBUTE = STARRED = NAMECONSTANT = \
    NAMEDEXPR = handleChildren


EKR

Edward K. Ream

unread,
Nov 27, 2025, 9:13:45 AM (3 days ago) Nov 27
to leo-editor
On Thursday, November 27, 2025 at 5:20:15 AM UTC-6 Edward K. Ream wrote:

> Working directly in site-packages/pyflakes is turning out to be an excellent (if eccentric) strategy.  It's liberating to use live code without cloning or forking a repo.

I've created the ekr-live-pyflakes repo for this work. As stated in the readme, this repo will be my private playground for the foreseeable future.

> Any changes to the ATTRIBUTE function take effect immediately, without calling importlib.reload 

Note that non-patched changes take effect only after reloading Leo (or after importlib.reload). So far, the only such change is pylint.__init__.py:

# True only when running the test-pyflakes script.
trace = False

My test script now contains:

try:
    pyflakes.trace = True
    check(test, filename='pyflakes_test.py')
finally:
    pyflakes.trace = False


The patched ATTRIBUTE function is now:

def ATTRIBUTE(self, node) -> None:
    if isinstance(node.value, ast.Name):
        if pyflakes.trace:

            g.trace(
                f"{node.ctx.__class__.__name__}"
                f"{node.value.id}.{node.attr}"
            )
    self.handleChildren(node)


Summary

This is a supremely light and flexible framework:

- I expect all experiments will occur in patched code. They have no effect on production code such as my pyflakes-leo.cmd script.
- I back up all code with a push from Python's site-packages/pyflakes directory.
- Nothing I do will have any effect on pyflakes itself. I can create my own issues in my repo.

This is the workflow of my dreams. Onward to the study and improvement of pyflakes!

Edward

Thomas Passin

unread,
Nov 28, 2025, 12:32:59 AM (3 days ago) Nov 28
to leo-editor
Very cool to monkeypatch a class. It makes sense - good old Python! I don't remember if I ever patched a class but I have monkeypatched module variables.  I remember that I developed Leo's highlighting of the current line and the right margin guideline mostly by monkeypatching instances using scripts in my workbook.  Some of the functionality of my GF4 Graphic Calculator application is provided by what is in effect monkeypatching additional commands into the dispatching class. That is done by importing the modules inside the class definition so that their "self" parameter when called will be the class instance. 

Edward K. Ream

unread,
Nov 28, 2025, 6:32:24 PM (2 days ago) Nov 28
to leo-editor
On Thursday, November 27, 2025 at 5:20:15 AM UTC-6 Edward K. Ream wrote:

> Aha!  g.funcToMethod (a thin wrapper for setattr) can be applied to classes, not just class instances! 

A milestone. The new ATTRIBUTE visitor checks for the attributes of the Commands, Position, and leoGlobals modules as follows, where leoC, leoG, and leoP are the live c, g, and p objects passed to my @button test script:

def ATTRIBUTE(self, node) -> None:
    if isinstance(node.value, ast.Name):
        base = node.value.id
        attr = node.attr
        table = (
            (leoC, ('c', 'c1', 'c2')),
            (leoG, ('g', 'leoGlobals')),
            (leoP, ('p', 'p1', 'p2')),
        )
        for obj, bases in table:
            if base in bases and not hasattr(obj, attr):
                self.report(messages.UndefinedName, node, f"{base}.{attr}")
                return  # Otherwise pyflakes reports both base and attr as changed.

    self.handleChildren(node)

g.funcToMethod(ATTRIBUTE, Checker)


A check of the entire leoApp.py file shows that this no code makes no noticeable difference to pyflakes's speed. The patched pyflakes takes 0.05 sec.

This hack does about 90% of what I've ever wanted an enhanced pylint to do!

Edward

Edward K. Ream

unread,
Nov 29, 2025, 7:13:06 AM (yesterday) Nov 29
to leo-editor
On Friday, November 28, 2025 at 5:32:24 PM UTC-6 Edward K. Ream wrote:

> A milestone. The new ATTRIBUTE visitor...does about 90% of what I've ever wanted an enhanced pylint to do!

On second thought, this statement is a useful exaggeration. I have just created #4483, suggesting a new script, check_leo.py, that would check all of Leo's core for attribute errors not found by mypy, pyflakes or pylint. This script should be extremely fast—about as fast pyflakes. We'll see about that.

Edward



Edward K. Ream

unread,
Nov 29, 2025, 6:29:36 PM (21 hours ago) Nov 29
to leo-editor
On Saturday, November 29, 2025 at 6:13:06 AM UTC-6 Edward K. Ream wrote:

> A milestone. The new ATTRIBUTE visitor...does about 90% of what I've ever wanted an enhanced pylint to do!

PR #4484 is already a success! See The check_leo.py script found two real bugs in leoserver.py!

The script also suggested several stylistic improvements. See the PR for details.

Much more work is coming, but I am already pleased with the results.

Edward
Reply all
Reply to author
Forward
0 new messages