pawamoy
pawamoy

Reputation: 3806

Extending ast.AST

I'm writing a Python tool that walks an AST to extract some data. To improve its usability, I want to extend the ast.AST class, so every node that is based on it has the same ability, for example getting the parent node, the children, the siblings, etc.

On Python 3.9+, I'm able to attach additional attributes, methods and cached properties to ast.AST directly:

def _attach_to(cls, name=None):
    def decorator(func):
        func_name = name or func.__name__
        cached = cached_property(func)
        setattr(cls, func_name, cached)
        # see https://bugs.python.org/issue38524
        cached.__set_name__(cls, func_name)  # noqa: WPS609
        return func
    return decorator

But on Python 3.8, which I'm willing to support, I cannot attach additional things to ast.AST which is a builtin:

TypeError: can't set attributes of built-in/extension type '_ast.AST'

So I explored different things:

Is there a way to dynamically add attributes/methods/properties to built-in classes?

Upvotes: 1

Views: 311

Answers (1)

pawamoy
pawamoy

Reputation: 3806

I eventually found something that works: I declare an ExtendedAST class, but instead of making it a subclass of ast.AST, I leave it as a "mixin" and append it to the __bases__ of all the different nodes' classes:

for name, member in inspect.getmembers(ast):
    if name != "AST" and inspect.isclass(member):
        if ast.AST in member.__bases__:
            new_bases = list(member.__bases__)
            new_bases.append(ExtendedAST)
            member.__bases__ = tuple(new_bases)

Upvotes: 1

Related Questions