Skip to content

Commit a8952bb

Browse files
Record: make __repr__ deterministic
1 parent ee878be commit a8952bb

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

pytools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ def __repr__(self):
448448
return "{}({})".format(
449449
self.__class__.__name__,
450450
", ".join(f"{fld}={getattr(self, fld)!r}"
451-
for fld in self.__class__.fields
451+
for fld in sorted(self.__class__.fields)
452452
if hasattr(self, fld)))
453453

454454
def register_fields(self, new_fields):

pytools/test/test_pytools.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import sys
2626

2727
import pytest
28+
from pytools import Record
2829

2930

3031
logger = logging.getLogger(__name__)
@@ -783,6 +784,78 @@ def test_unique():
783784
assert next(unique([]), None) is None
784785

785786

787+
# This class must be defined globally to be picklable
788+
class SimpleRecord(Record):
789+
pass
790+
791+
792+
def test_record():
793+
r = SimpleRecord(c=3, b=2, a=1)
794+
795+
assert r.a == 1
796+
assert r.b == 2
797+
assert r.c == 3
798+
799+
# Fields are sorted alphabetically in records
800+
assert str(r) == "SimpleRecord(a=1, b=2, c=3)"
801+
802+
# Unregistered fields are (silently) ignored for printing
803+
r.f = 6
804+
assert str(r) == "SimpleRecord(a=1, b=2, c=3)"
805+
806+
# Registered fields are printed
807+
r.register_fields({"d", "e"})
808+
assert str(r) == "SimpleRecord(a=1, b=2, c=3)"
809+
810+
r.d = 4
811+
r.e = 5
812+
assert str(r) == "SimpleRecord(a=1, b=2, c=3, d=4, e=5)"
813+
814+
with pytest.raises(AttributeError):
815+
r.ff
816+
817+
# Test pickling
818+
import pickle
819+
r_pickled = pickle.loads(pickle.dumps(r))
820+
assert r == r_pickled
821+
822+
# }}}
823+
824+
# {{{ __slots__, __dict__, __weakref__ handling
825+
826+
class RecordWithEmptySlots(Record):
827+
__slots__ = []
828+
829+
assert hasattr(RecordWithEmptySlots(), "__slots__")
830+
assert not hasattr(RecordWithEmptySlots(), "__dict__")
831+
assert not hasattr(RecordWithEmptySlots(), "__weakref__")
832+
833+
class RecordWithUnsetSlots(Record):
834+
pass
835+
836+
assert hasattr(RecordWithUnsetSlots(), "__slots__")
837+
assert hasattr(RecordWithUnsetSlots(), "__dict__")
838+
assert hasattr(RecordWithUnsetSlots(), "__weakref__")
839+
840+
from pytools import ImmutableRecord
841+
842+
class ImmutableRecordWithEmptySlots(ImmutableRecord):
843+
__slots__ = []
844+
845+
assert hasattr(ImmutableRecordWithEmptySlots(), "__slots__")
846+
assert hasattr(ImmutableRecordWithEmptySlots(), "__dict__")
847+
assert hasattr(ImmutableRecordWithEmptySlots(), "__weakref__")
848+
849+
class ImmutableRecordWithUnsetSlots(ImmutableRecord):
850+
pass
851+
852+
assert hasattr(ImmutableRecordWithUnsetSlots(), "__slots__")
853+
assert hasattr(ImmutableRecordWithUnsetSlots(), "__dict__")
854+
assert hasattr(ImmutableRecordWithUnsetSlots(), "__weakref__")
855+
856+
# }}}
857+
858+
786859
if __name__ == "__main__":
787860
if len(sys.argv) > 1:
788861
exec(sys.argv[1])

0 commit comments

Comments
 (0)