erikrose / parsimonious

The fastest pure-Python PEG parser I can muster
MIT License
1.8k stars 126 forks source link

Support functions, class methods, and descriptors for custom rules #157

Closed jayaddison closed 3 years ago

jayaddison commented 4 years ago

This PR is to handle a fairly specific scenario where a Grammar is defined inside a Python class, and refers to custom rules which are static methods on that class.

In parsimonious==0.8.1, attempting the following will fail:

class ExampleParser():
    @staticmethod
    def custom_rule(text, pos):
        pass

    grammar = Grammar("""
        ...
        """,
        custom_rule=custom_rule
    )

The failure occurs when an exception is raised during creation of the rule map, and the root cause is that the reference to custom_rule is resolved by Python as an unbound method descriptor.

This PR resolves the issue by adding a specific check to see if a rule matches the inspect.ismethoddescriptor check; if it does, we also need to retrieve the __func__ value from the reference in order to obtain a Python callable.

The PR also handles a similar case where a (non-static) class method is bound to a custom rule, but an argument count mismatch occurs due to the presence of the instance self argument in object method calls.

Edits: enable syntax highlighting, fix indenting, make syntactically valid

jayaddison commented 4 years ago

cc @erikrose - I've been experimenting with using custom rules as part of a small ingredient parsing grammar and ran into issues when using class-methods as handlers, which I've attempted to address here. Glad to make any adjustments and modifications; thanks for the library!

jayaddison commented 4 years ago

Small nudge @erikrose - do you have any time for review of this PR? If the change doesn't seem appropriate or there are any issues with it, I'm happy to close or modify it.

jayaddison commented 4 years ago

Thanks for the review @evanh @erikrose! Hopefully mostly set, glad to apply any further cleanups.

erikrose commented 3 years ago

Thanks for the great patch, @jayaddison, and for the great reviews, @evanh! We're finally starting to get things unstuck around here. :-)