When defining complex schemas, you may need to handle recursive or mutually-recursive types:
- Self-references: Use
DataType.OBJECT.self as a shorthand sentinel to refer back to the enclosing object's schema. - Forward references: Use
DataType.OBJECT.reference('TypeName') to create a placeholder for a type that hasn't been defined yet. - Mutual recursion: For types that refer to each other (e.g.,
Person refers to Company and Company refers to Person), place both types in the type_resolver dictionary of the rule_engine.Context. The references will then be resolved lazily at rule parse time.
# Self-reference example
Hero = rule_engine.DataType.OBJECT('Hero', attributes={
'name': rule_engine.DataType.STRING,
'nemesis': rule_engine.DataType.OBJECT.self, # resolved to Hero
})
# Mutual recursion example
Person = rule_engine.DataType.OBJECT('Person', attributes={
'name': rule_engine.DataType.STRING,
'employer': rule_engine.DataType.OBJECT.reference('Company'),
})
Company = rule_engine.DataType.OBJECT('Company', attributes={
'name': rule_engine.DataType.STRING,
'ceo': rule_engine.DataType.OBJECT.reference('Person'),
})
context = rule_engine.Context(type_resolver={
'employee': Person,
'Person': Person,
'Company': Company,
})
rule = rule_engine.Rule('employee.employer.ceo.name == "Palpatine"', context=context)