I have a design question regarding "dynamic information hiding" vs static typing.
I have for the following python code
class RuleInput:
attr1 = ...
atrr2 = ...
...
def example_rule(input: RuleInput) -> bool:
# does something and returns bool
I have many of these rule functions, each calculating its output based on the attributes in RuleInput
(let's assume it's immutable).
For this code, it is clear what the input should be and tools like mypy
can be used to do static type checking. Here also features like auto-completion work.
However, most of the rules only require a small number of attributes from the RuleInput
. So developers could make mistakes by accessing the wrong information.
Hence, I had the idea of dynamically reducing the content of the RuleInput
specific to each rule (rules just specify what they need beforehand). When doing this I lose the possibility of performing static type checking and features like auto-completion.
Here is the "dynamic information hiding" solution:
RULE_INPUTS = [
('attr1', int, field(default=0)),
('attr2', int, field(default=0))
]
RuleInputNames = Enum(
"RuleInputNames",
" ".join([x[0] for x in RULE_INPUTS])
)
def check(required_props):
def extract_required_properties(full_input, required_properties):
all_props = asdict(full_input)
selected_props = {}
for key in required_properties:
selected_props[key] = all_props[key]
return selected_props
req_props = [x.name for x in required_props]
RequiredProperties = make_dataclass(
"RequiredProperties",
[x for x in RULE_INPUTS if x[0] in req_props]
)
def decorator(func):
def wrapper(full_rule_input):
return func(
RequiredProperties(
**extract_required_properties(full_rule_input,
req_edge_props),
)
)
return wrapper
return decorator
@rule(required_props=[ParticlePropertyNames.attr1])
def some_rule(rule_input):
rule_input.attr1 # works
rule_input.attr2 # wont work
What do you think about this "dynamic information hiding" vs static type checking? Which of these two approaches would you recommend?