Skip to content

Commit 0a4cfe1

Browse files
matthiasdienerinducer
authored andcommitted
Record: make __repr__ deterministic
1 parent ee878be commit 0a4cfe1

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
import pytest
2828

29+
from pytools import Record
30+
2931

3032
logger = logging.getLogger(__name__)
3133
from typing import FrozenSet
@@ -783,6 +785,78 @@ def test_unique():
783785
assert next(unique([]), None) is None
784786

785787

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

0 commit comments

Comments
 (0)