Search code examples
pythonpython-typingmypytextx

typechecking for textx models


I am trying out a prototype for a language using the textx module for python that can interpret grammars. I am creating a model like this:

from textx import metamodel_from_str

funl_grammar = """
    Model: statement *= Statement;

    Statement: FunctionDefinition | Function;

    FunctionDefinition: name=ID '=' function=Function;
    Function: name=ID '(' params*=Param (',' params*=Param)* ')';

    Param: Function | INT | ID | STRING;
    Comment: /#.*$/;
    ID: /[a-zA-Z_][a-zA-Z0-9_]*/;
    INT: /[0-9]+/;
    STRING: /".*"/;
"""

mm = metamodel_from_str(funl_grammar)

Now my question is, how can I typecheck for it using mypy?

If I create a function like this where I want to be sure that the parameter is part of my grammar and textx considers it a Param:

def my_func(param: mm["Param"]):
    ...

I get the mypy error error: Name "Param" is not defined [name-defined].

Do you know how I could fix that? Googling and ChatGPT has not helped so far.


Solution

  • The library in question is creating these classes dynamically:

    def _new_class(self, ...):
        class TextXClass(metaclass=TextXMetaClass):
            def __repr__(self): ...
    
        cls = TextXClass
        cls.__name__ = name
    
        self._init_class(cls, ...)
    
        return cls
    

    They are created inside a closure, and only as instances of TextXMetaClass. It is thus not possible to refer to them statically in any other ways than TextXMetaClass, which itself doesn't have much information to begin with:

    class TextXMetaClass(type):
        def __repr__(cls): ...
    
    # `param` must be a class created by TextXMetaClass
    def my_func(param: TextXMetaClass): ...
    
    my_func(mm["Param"])    # This is supposedly fine
    my_func(mm["Param"]())  # This is not
    

    TextX is also untyped, and a Mypy plugin for it doesn't seem to exist either, so your best bet is to forgo static type checking altogether.