repred reads __init__ source/AST and infers which instance attributes represent constructor parameters. We found a case where this inference appears ambiguous, but no exception is raised.
Example:
@repred(positionals=['slot_function'])
class Slot:
def __init__(self, slot_function, slot_name):
self.slot_function = slot_function
self.slot_name = slot_name if slot_name is not None else slot_function.__name__
Here self.slot_name can be populated from either slot_name or slot_function.__name__. Without an explicit getter, repred silently uses self.slot_name as the representation value for the slot_name parameter. This changes the repr from the intended "only show explicitly provided slot_name" behavior to showing the fallback value as if it had been provided by the caller.
It would be safer if repred raised an exception when more than one constructor parameter can contribute to a represented field, unless the user provides an explicit getter for that field.
Possible minimal regression test:
import pytest
from printo import repred
def test_repred_rejects_ambiguous_field_mapping_without_getter():
with pytest.raises(Exception): # ideally a dedicated printo ambiguity error
@repred(positionals=['function'])
class Example:
def __init__(self, function, name):
self.function = function
self.name = name if name is not None else function.__name__
def test_repred_accepts_ambiguous_field_mapping_with_explicit_getter():
@repred(
positionals=['function'],
getters={'name': lambda x: x.declared_name},
)
class Example:
def __init__(self, function, name):
self.function = function
self.declared_name = name
self.name = name if name is not None else function.__name__
def default_name():
...
assert repr(Example(default_name, None)) == 'Example(default_name)'
assert repr(Example(default_name, 'custom')) == "Example(default_name, name='custom')"
The first test should fail against the current behavior because repred accepts the ambiguous mapping and produces a repr using the normalized fallback value.
Expected behavior:
@repred(
positionals=['slot_function'],
getters={'slot_name': lambda x: x.declared_slot_name},
)
class Slot:
...
In other words, ambiguous AST mappings should fail closed and require the class author to spell out the intended representation value.
repredreads__init__source/AST and infers which instance attributes represent constructor parameters. We found a case where this inference appears ambiguous, but no exception is raised.Example:
Here
self.slot_namecan be populated from eitherslot_nameorslot_function.__name__. Without an explicit getter,repredsilently usesself.slot_nameas the representation value for theslot_nameparameter. This changes the repr from the intended "only show explicitly provided slot_name" behavior to showing the fallback value as if it had been provided by the caller.It would be safer if
repredraised an exception when more than one constructor parameter can contribute to a represented field, unless the user provides an explicit getter for that field.Possible minimal regression test:
The first test should fail against the current behavior because
repredaccepts the ambiguous mapping and produces a repr using the normalized fallback value.Expected behavior:
In other words, ambiguous AST mappings should fail closed and require the class author to spell out the intended representation value.