Structural Pattern Matching#

PyBryt supports structural pattern matching in order to allow you to create annotations that check the structure of objects instead of using an == check. Structural patterns can be created by accessing attributes of the singleton pybryt.structural and calling them with attribute-value pairs as arguments. For example, if you’re matching an instance of mypackage.Foo with attribute bar set to 2; a structural pattern for this could be created with

pybryt.structural.mypackage.Foo(bar=2)

If there are attributes you want to look for without a specific name, you can pass these as positional arguments:

pybryt.structural.mypackage.Foo(3, bar=2)

To determine whether an object matches the structural pattern, PyBryt imports the package and retrieves the specified class. In the examples above, this would look like

getattr(importlib.import_module("mypackage"), "Foo")

If the provided object is an instance of this class and has the specified attributes, the object matches. You can determine if an object matches a structural pattern using an == comparison.

If no package is specified for the class, the pattern just checks that the name of the class matches the name of the class in the structural pattern, without importing any modules. For example:

df_pattern = pybryt.structural.DataFrame()
df_pattern == pd.DataFrame()  # returns True

class DataFrame:
    pass

df_pattern == DataFrame()     # returns True

Attribute values are matched using the same algorithm as Value annotations. If you would like to make use of the options available to Value annotations, you can also pass an annotation as an attribute value:

pybryt.structural.mypackage.Foo(pi=pybryt.Value(np.pi, atol=1e-5))

For checking whether an object contains specific members (determined via the use of Python’s in operator), use the contains_ method:

pybryt.structural.mypackage.MyList().contains_(1, 2, 3)

To use structural patterns, pass them as values to Value annotations. When a value annotation is checking for a structural pattern, it uses the pattern’s == check to determine whether any object in the memory footprint matches.

pybryt.Value(pybryt.structural.mypackage.MyList())