Repository: amaranth-lang/amaranth
Pull request #1423 (2024-06-27 14:37:37)

Bulk-replace `cls.__name__` with `cls.__qualname__` [0.5 backport]
In most cases these two attributes contain the same string, however, in case of nested or locally defined classes, the latter is important for disambiguation.

A few instances of `mod.__name__` remain in the codebase.


Patch:
diff --git a/amaranth/_unused.py b/amaranth/_unused.py
index 6e05343..4f2507b 100644
--- a/amaranth/_unused.py
+++ b/amaranth/_unused.py
@@ -32,7 +32,7 @@ class MustUse:
             return
         if hasattr(self, "_MustUse__used") and not self._MustUse__used:
             if get_linter_option(self._MustUse__context["filename"],
-                                 self._MustUse__warning.__name__, bool, True):
+                                 self._MustUse__warning.__qualname__, bool, True):
                 warnings.warn_explicit(
                     f"{self!r} created but never used", self._MustUse__warning,
                     **self._MustUse__context)
diff --git a/amaranth/_utils.py b/amaranth/_utils.py
index 9384615..10b125b 100644
--- a/amaranth/_utils.py
+++ b/amaranth/_utils.py
@@ -44,7 +44,7 @@ def union(i, start=None):
 def final(cls):
     def init_subclass():
         raise TypeError("Subclassing {}.{} is not supported"
-                        .format(cls.__module__, cls.__name__))
+                        .format(cls.__module__, cls.__qualname__))
     cls.__init_subclass__ = init_subclass
     return cls
 
diff --git a/amaranth/build/plat.py b/amaranth/build/plat.py
index 432ae67..d5b02ab 100644
--- a/amaranth/build/plat.py
+++ b/amaranth/build/plat.py
@@ -40,7 +40,7 @@ class Platform(ResourceManager, metaclass=ABCMeta):
     def default_clk_constraint(self):
         if self.default_clk is None:
             raise AttributeError("Platform '{}' does not define a default clock"
-                                 .format(type(self).__name__))
+                                 .format(type(self).__qualname__))
         return self.lookup(self.default_clk).clock
 
     @property
@@ -48,7 +48,7 @@ class Platform(ResourceManager, metaclass=ABCMeta):
         constraint = self.default_clk_constraint
         if constraint is None:
             raise AttributeError("Platform '{}' does not constrain its default clock"
-                                 .format(type(self).__name__))
+                                 .format(type(self).__qualname__))
         return constraint.frequency
 
     def add_file(self, filename, content):
@@ -173,7 +173,7 @@ class Platform(ResourceManager, metaclass=ABCMeta):
         Extract bitstream for fragment ``name`` from ``products`` and download it to a target.
         """
         raise NotImplementedError("Platform '{}' does not support programming"
-                                  .format(type(self).__name__))
+                                  .format(type(self).__qualname__))
 
 
 class TemplatedPlatform(Platform):
@@ -244,7 +244,7 @@ class TemplatedPlatform(Platform):
                 if issubclass(expected_type, str) and not isinstance(kwarg, str) and isinstance(kwarg, Iterable):
                     kwarg = " ".join(kwarg)
                 if not isinstance(kwarg, expected_type) and not expected_type is None:
-                    raise TypeError(f"Override '{var}' must be a {expected_type.__name__}, not {kwarg!r}")
+                    raise TypeError(f"Override '{var}' must be a {expected_type.__qualname__}, not {kwarg!r}")
                 return kwarg
             else:
                 return jinja2.Undefined(name=var)
diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py
index 8eea927..8923675 100644
--- a/amaranth/hdl/_ast.py
+++ b/amaranth/hdl/_ast.py
@@ -244,16 +244,16 @@ class ShapeCastable:
 
     def __init_subclass__(cls, **kwargs):
         if cls.as_shape is ShapeCastable.as_shape:
-            raise TypeError(f"Class '{cls.__name__}' deriving from 'ShapeCastable' must override "
+            raise TypeError(f"Class '{cls.__qualname__}' deriving from 'ShapeCastable' must override "
                             f"the 'as_shape' method")
         if cls.const is ShapeCastable.const:
-            raise TypeError(f"Class '{cls.__name__}' deriving from 'ShapeCastable' must override "
+            raise TypeError(f"Class '{cls.__qualname__}' deriving from 'ShapeCastable' must override "
                             f"the 'const' method")
         if cls.__call__ is ShapeCastable.__call__:
-            raise TypeError(f"Class '{cls.__name__}' deriving from 'ShapeCastable' must override "
+            raise TypeError(f"Class '{cls.__qualname__}' deriving from 'ShapeCastable' must override "
                             f"the '__call__' method")
         if cls.from_bits is ShapeCastable.from_bits:
-            warnings.warn(f"Class '{cls.__name__}' deriving from 'ShapeCastable' does not override "
+            warnings.warn(f"Class '{cls.__qualname__}' deriving from 'ShapeCastable' does not override "
                           f"the 'from_bits' method, which will be required in Amaranth 0.6",
                           DeprecationWarning, stacklevel=2)
 
@@ -1404,10 +1404,10 @@ class ValueCastable:
 
     def __init_subclass__(cls, **kwargs):
         if cls.as_value is ValueCastable.as_value:
-            raise TypeError(f"Class '{cls.__name__}' deriving from 'ValueCastable' must override "
+            raise TypeError(f"Class '{cls.__qualname__}' deriving from 'ValueCastable' must override "
                             "the 'as_value' method")
         if cls.shape is ValueCastable.shape:
-            raise TypeError(f"Class '{cls.__name__}' deriving from 'ValueCastable' must override "
+            raise TypeError(f"Class '{cls.__qualname__}' deriving from 'ValueCastable' must override "
                             "the 'shape' method")
 
     # The signatures and definitions of these methods are weird because they are present here for
@@ -2095,12 +2095,12 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
         if isinstance(orig_shape, ShapeCastable):
             self._format = orig_shape.format(orig_shape(self), "")
         elif isinstance(orig_shape, type) and issubclass(orig_shape, Enum):
-            self._format = Format.Enum(self, orig_shape, name=orig_shape.__name__)
+            self._format = Format.Enum(self, orig_shape, name=orig_shape.__qualname__)
         else:
             self._format = Format("{}", self)
 
         if isinstance(decoder, type) and issubclass(decoder, Enum):
-            self._format = Format.Enum(self, decoder, name=decoder.__name__)
+            self._format = Format.Enum(self, decoder, name=decoder.__qualname__)
 
         self._decoder = decoder
 
@@ -3241,7 +3241,7 @@ class _MappedKeyDict(MutableMapping, _MappedKeyCollection):
 
     def __repr__(self):
         pairs = [f"({k!r}, {v!r})" for k, v in self.items()]
-        return "{}.{}([{}])".format(type(self).__module__, type(self).__name__,
+        return "{}.{}([{}])".format(type(self).__module__, type(self).__qualname__,
                                     ", ".join(pairs))
 
 
@@ -3273,7 +3273,7 @@ class _MappedKeySet(MutableSet, _MappedKeyCollection):
         return len(self._storage)
 
     def __repr__(self):
-        return "{}.{}({})".format(type(self).__module__, type(self).__name__,
+        return "{}.{}({})".format(type(self).__module__, type(self).__qualname__,
                                   ", ".join(repr(x) for x in self))
 
 
@@ -3303,7 +3303,7 @@ class SignalKey:
         return self._intern < other._intern
 
     def __repr__(self):
-        return f"<{__name__}.SignalKey {self.signal!r}>"
+        return f"<{__qualname__}.SignalKey {self.signal!r}>"
 
 
 class SignalDict(_MappedKeyDict):
diff --git a/amaranth/hdl/_dsl.py b/amaranth/hdl/_dsl.py
index c66df2e..6de5b1e 100644
--- a/amaranth/hdl/_dsl.py
+++ b/amaranth/hdl/_dsl.py
@@ -144,9 +144,9 @@ class _ModuleBuilderRoot:
     def __getattr__(self, name):
         if name in ("comb", "sync"):
             raise AttributeError("'{}' object has no attribute '{}'; did you mean 'd.{}'?"
-                                 .format(type(self).__name__, name, name))
+                                 .format(type(self).__qualname__, name, name))
         raise AttributeError("'{}' object has no attribute '{}'"
-                             .format(type(self).__name__, name))
+                             .format(type(self).__qualname__, name))
 
 
 class _ModuleBuilderSubmodules:
diff --git a/amaranth/hdl/_nir.py b/amaranth/hdl/_nir.py
index 7f9c268..2cd4d28 100644
--- a/amaranth/hdl/_nir.py
+++ b/amaranth/hdl/_nir.py
@@ -465,7 +465,7 @@ class Netlist:
                     elif isinstance(obj, Operator):
                         obj = f"operator {obj.operator}"
                     else:
-                        obj = f"cell {obj.__class__.__name__}"
+                        obj = f"cell {obj.__class__.__qualname__}"
                     src_loc = "<unknown>:0" if src_loc is None else f"{src_loc[0]}:{src_loc[1]}"
                     msg.append(f"  {src_loc}: {obj} bit {bit}\n")
                 raise CombinationalCycle("".join(msg))
@@ -656,7 +656,7 @@ class Operator(Cell):
 
     The ternary operators are:
 
-    - 'm': multiplexer, first input needs to have width of 1, second and third operand need to have 
+    - 'm': multiplexer, first input needs to have width of 1, second and third operand need to have
       the same width as output; implements arg0 ? arg1 : arg2
 
     Attributes
diff --git a/amaranth/lib/cdc.py b/amaranth/lib/cdc.py
index 1afcc76..9939c38 100644
--- a/amaranth/lib/cdc.py
+++ b/amaranth/lib/cdc.py
@@ -96,7 +96,7 @@ class FFSynchronizer(Elaboratable):
         if self._max_input_delay is not None:
             raise NotImplementedError("Platform '{}' does not support constraining input delay "
                                       "for FFSynchronizer"
-                                      .format(type(platform).__name__))
+                                      .format(type(platform).__qualname__))
 
         m = Module()
         flops = [Signal(self.i.shape(), name=f"stage{index}",
@@ -170,7 +170,7 @@ class AsyncFFSynchronizer(Elaboratable):
         if self._max_input_delay is not None:
             raise NotImplementedError("Platform '{}' does not support constraining input delay "
                                       "for AsyncFFSynchronizer"
-                                      .format(type(platform).__name__))
+                                      .format(type(platform).__qualname__))
 
         m = Module()
         m.domains += ClockDomain("async_ff", async_reset=True, local=True)
diff --git a/amaranth/lib/data.py b/amaranth/lib/data.py
index 965dbff..1a6a3f7 100644
--- a/amaranth/lib/data.py
+++ b/amaranth/lib/data.py
@@ -911,7 +911,7 @@ class View(ValueCastable):
     __rxor__ = __and__
 
     def __repr__(self):
-        return f"{self.__class__.__name__}({self.__layout!r}, {self.__target!r})"
+        return f"{self.__class__.__qualname__}({self.__layout!r}, {self.__target!r})"
 
 
 class Const(ValueCastable):
@@ -1146,7 +1146,7 @@ class Const(ValueCastable):
     __rxor__ = __and__
 
     def __repr__(self):
-        return f"{self.__class__.__name__}({self.__layout!r}, {self.__target!r})"
+        return f"{self.__class__.__qualname__}({self.__layout!r}, {self.__target!r})"
 
 
 class _AggregateMeta(ShapeCastable, type):
diff --git a/amaranth/lib/enum.py b/amaranth/lib/enum.py
index 032f281..d6463c3 100644
--- a/amaranth/lib/enum.py
+++ b/amaranth/lib/enum.py
@@ -178,7 +178,7 @@ class EnumType(ShapeCastable, py_enum.EnumMeta):
     def format(cls, value, format_spec):
         if format_spec != "":
             raise ValueError(f"Format specifier {format_spec!r} is not supported for enums")
-        return Format.Enum(value, cls, name=cls.__name__)
+        return Format.Enum(value, cls, name=cls.__qualname__)
 
 
 # In 3.11, Python renamed EnumMeta to EnumType. Like Python itself, we support both for
@@ -310,7 +310,7 @@ class EnumView(ValueCastable):
         return self.target != other.target
 
     def __repr__(self):
-        return f"{type(self).__name__}({self.enum.__name__}, {self.target!r})"
+        return f"{type(self).__qualname__}({self.enum.__qualname__}, {self.target!r})"
 
 
 class FlagView(EnumView):
diff --git a/amaranth/lib/wiring.py b/amaranth/lib/wiring.py
index 1bf963e..b91a0e7 100644
--- a/amaranth/lib/wiring.py
+++ b/amaranth/lib/wiring.py
@@ -1198,7 +1198,7 @@ class PureInterface:
         attrs = ''.join(f", {name}={value!r}"
                         for name, value in self.__dict__.items()
                         if name != "signature")
-        return f'<{type(self).__name__}: {self.signature}{attrs}>'
+        return f'<{type(self).__qualname__}: {self.signature}{attrs}>'
 
 
 # To reduce API surface area `FlippedInterface` is made final. This restriction could be lifted
diff --git a/amaranth/rpc.py b/amaranth/rpc.py
index af0f125..ac3d2cf 100644
--- a/amaranth/rpc.py
+++ b/amaranth/rpc.py
@@ -77,7 +77,7 @@ def _serve_yosys(modules):
                 rtlil_text = rtlil.convert(elaboratable, name=module_name, ports=ports)
                 response = {"frontend": "ilang", "source": rtlil_text}
             except Exception as error:
-                response = {"error": f"{type(error).__name__}: {str(error)}"}
+                response = {"error": f"{type(error).__qualname__}: {str(error)}"}
 
         else:
             return {"error": "Unrecognized method {!r}".format(request["method"])}
diff --git a/amaranth/vendor/_xilinx.py b/amaranth/vendor/_xilinx.py
index 9a44c62..b952ff4 100644
--- a/amaranth/vendor/_xilinx.py
+++ b/amaranth/vendor/_xilinx.py
@@ -1236,7 +1236,7 @@ class XilinxPlatform(TemplatedPlatform):
         elif ff_sync._max_input_delay is not None:
             raise NotImplementedError("Platform '{}' does not support constraining input delay "
                                       "for FFSynchronizer"
-                                      .format(type(self).__name__))
+                                      .format(type(self).__qualname__))
         for i, o in zip((ff_sync.i, *flops), flops):
             m.d[ff_sync._o_domain] += o.eq(i)
         m.d.comb += ff_sync.o.eq(flops[-1])
@@ -1284,7 +1284,7 @@ class XilinxPlatform(TemplatedPlatform):
             elif async_ff_sync._max_input_delay is not None:
                 raise NotImplementedError("Platform '{}' does not support constraining input delay "
                                           "for AsyncFFSynchronizer"
-                                          .format(type(self).__name__))
+                                          .format(type(self).__qualname__))
 
         for i, o in zip((0, *flops_q), flops_d):
             m.d.comb += o.eq(i)


Test patch:
diff --git a/tests/test_back_rtlil.py b/tests/test_back_rtlil.py
index 6c4dbcc..a52f7ce 100644
--- a/tests/test_back_rtlil.py
+++ b/tests/test_back_rtlil.py
@@ -2024,7 +2024,7 @@ class DetailTestCase(RTLILTestCase):
         attribute \generator "Amaranth"
         attribute \top 1
         module \top
-            attribute \enum_base_type "MyEnum"
+            attribute \enum_base_type "DetailTestCase.test_enum.<locals>.MyEnum"
             attribute \enum_value_00 "A"
             attribute \enum_value_01 "B"
             attribute \enum_value_10 "C"
@@ -2052,7 +2052,7 @@ class DetailTestCase(RTLILTestCase):
         attribute \top 1
         module \top
             wire width 13 input 0 \sig
-            attribute \enum_base_type "MyEnum"
+            attribute \enum_base_type "DetailTestCase.test_struct.<locals>.MyEnum"
             attribute \enum_value_00 "A"
             attribute \enum_value_01 "B"
             attribute \enum_value_10 "C"
diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py
index 666968f..d440d8f 100644
--- a/tests/test_hdl_ast.py
+++ b/tests/test_hdl_ast.py
@@ -185,7 +185,7 @@ class MockShapeCastable(ShapeCastable):
 class ShapeCastableTestCase(FHDLTestCase):
     def test_no_override(self):
         with self.assertRaisesRegex(TypeError,
-                r"^Class 'MockShapeCastableNoOverride' deriving from 'ShapeCastable' must "
+                r"^Class '.+\.MockShapeCastableNoOverride' deriving from 'ShapeCastable' must "
                 r"override the 'as_shape' method$"):
             class MockShapeCastableNoOverride(ShapeCastable):
                 def __init__(self):
@@ -213,7 +213,7 @@ class ShapeCastableTestCase(FHDLTestCase):
 
     def test_no_from_bits(self):
         with self.assertWarnsRegex(DeprecationWarning,
-                r"^Class 'MockShapeCastableNoFromBits' deriving from 'ShapeCastable' does "
+                r"^Class '.+\.MockShapeCastableNoFromBits' deriving from 'ShapeCastable' does "
                 r"not override the 'from_bits' method, which will be required in Amaranth 0.6$"):
             class MockShapeCastableNoFromBits(ShapeCastable):
                 def __init__(self, dest):
@@ -528,7 +528,7 @@ class ConstTestCase(FHDLTestCase):
 
             def shape(self):
                 return MockConstShape()
-            
+
             def as_value(self):
                 return Const(self.value, 8)
 
@@ -1327,7 +1327,7 @@ class SignalTestCase(FHDLTestCase):
             BLUE = 2
         s = Signal(decoder=Color)
         self.assertEqual(s.decoder, Color)
-        self.assertRepr(s._format, "(format-enum (sig s) 'Color' (1 'RED') (2 'BLUE'))")
+        self.assertRepr(s._format, f"(format-enum (sig s) '{Color.__qualname__}' (1 'RED') (2 'BLUE'))")
 
     def test_enum(self):
         s1 = Signal(UnsignedEnum)
@@ -1515,14 +1515,14 @@ class MockValueCastableCustomGetattr(ValueCastable):
 class ValueCastableTestCase(FHDLTestCase):
     def test_no_override(self):
         with self.assertRaisesRegex(TypeError,
-                r"^Class 'MockValueCastableNoOverrideAsValue' deriving from 'ValueCastable' must "
+                r"^Class '.+\.MockValueCastableNoOverrideAsValue' deriving from 'ValueCastable' must "
                 r"override the 'as_value' method$"):
             class MockValueCastableNoOverrideAsValue(ValueCastable):
                 def __init__(self):
                     pass
 
         with self.assertRaisesRegex(TypeError,
-                r"^Class 'MockValueCastableNoOverrideShapec' deriving from 'ValueCastable' must "
+                r"^Class '.+\.MockValueCastableNoOverrideShapec' deriving from 'ValueCastable' must "
                 r"override the 'shape' method$"):
             class MockValueCastableNoOverrideShapec(ValueCastable):
                 def __init__(self):
diff --git a/tests/test_hdl_ir.py b/tests/test_hdl_ir.py
index 5b6a682..cc5517e 100644
--- a/tests/test_hdl_ir.py
+++ b/tests/test_hdl_ir.py
@@ -3523,7 +3523,7 @@ class FieldsTestCase(FHDLTestCase):
         ])
         self.assertEqual(nl.signal_fields[s1.as_value()], {
             (): SignalField(nl.signals[s1.as_value()], signed=False),
-            ('a',): SignalField(nl.signals[s1.as_value()][0:2], signed=False, enum_name="MyEnum", enum_variants={
+            ('a',): SignalField(nl.signals[s1.as_value()][0:2], signed=False, enum_name=MyEnum.__qualname__, enum_variants={
                 0: "A",
                 1: "B",
                 2: "C",
@@ -3531,7 +3531,7 @@ class FieldsTestCase(FHDLTestCase):
             ('b',): SignalField(nl.signals[s1.as_value()][2:5], signed=True)
         })
         self.assertEqual(nl.signal_fields[s2.as_value()], {
-            (): SignalField(nl.signals[s2.as_value()], signed=False, enum_name="MyEnum", enum_variants={
+            (): SignalField(nl.signals[s2.as_value()], signed=False, enum_name=MyEnum.__qualname__, enum_variants={
                 0: "A",
                 1: "B",
                 2: "C",
@@ -3622,4 +3622,4 @@ class RequirePosedgeTestCase(FHDLTestCase):
         m.submodules += DomainRenamer("test")(RequirePosedge("sync"))
         with self.assertRaisesRegex(DomainRequirementFailed,
                 r"^Domain test has a negedge clock, but posedge clock is required by top.U\$0 at .*$"):
-            Fragment.get(m, None).prepare()
\ No newline at end of file
+            Fragment.get(m, None).prepare()
diff --git a/tests/test_lib_enum.py b/tests/test_lib_enum.py
index ce0c67d..1cfe1e2 100644
--- a/tests/test_lib_enum.py
+++ b/tests/test_lib_enum.py
@@ -107,9 +107,9 @@ class EnumTestCase(FHDLTestCase):
         class EnumA(Enum, shape=8):
             Z = 0
             A = 10
-        self.assertRepr(EnumA.const(None), "EnumView(EnumA, (const 8'd0))")
-        self.assertRepr(EnumA.const(10), "EnumView(EnumA, (const 8'd10))")
-        self.assertRepr(EnumA.const(EnumA.A), "EnumView(EnumA, (const 8'd10))")
+        self.assertRepr(EnumA.const(None), f"EnumView({EnumA.__qualname__}, (const 8'd0))")
+        self.assertRepr(EnumA.const(10), f"EnumView({EnumA.__qualname__}, (const 8'd10))")
+        self.assertRepr(EnumA.const(EnumA.A), f"EnumView({EnumA.__qualname__}, (const 8'd10))")
 
     def test_from_bits(self):
         class EnumA(Enum, shape=2):
@@ -138,7 +138,7 @@ class EnumTestCase(FHDLTestCase):
             B = -3
         a = Signal(EnumA)
         self.assertRepr(a, "(sig a)")
-        self.assertRepr(a._format, "(format-enum (sig a) 'EnumA' (0 'A') (-3 'B'))")
+        self.assertRepr(a._format, f"(format-enum (sig a) '{EnumA.__qualname__}' (0 'A') (-3 'B'))")
 
     def test_enum_view(self):
         class EnumA(Enum, shape=signed(4)):
@@ -153,7 +153,7 @@ class EnumTestCase(FHDLTestCase):
         d = Signal(4)
         self.assertIsInstance(a, EnumView)
         self.assertIs(a.shape(), EnumA)
-        self.assertRepr(a, "EnumView(EnumA, (sig a))")
+        self.assertRepr(a, f"EnumView({EnumA.__qualname__}, (sig a))")
         self.assertRepr(a.as_value(), "(sig a)")
         self.assertRepr(a.eq(c), "(eq (sig a) (sig c))")
         for op in [
@@ -214,7 +214,7 @@ class EnumTestCase(FHDLTestCase):
         c = Signal(FlagA)
         d = Signal(4)
         self.assertIsInstance(a, FlagView)
-        self.assertRepr(a, "FlagView(FlagA, (sig a))")
+        self.assertRepr(a, f"FlagView({FlagA.__qualname__}, (sig a))")
         for op in [
             operator.__add__,
             operator.__sub__,
@@ -260,17 +260,17 @@ class EnumTestCase(FHDLTestCase):
         self.assertRepr(a == FlagA.B, "(== (sig a) (const 4'd4))")
         self.assertRepr(FlagA.B == a, "(== (sig a) (const 4'd4))")
         self.assertRepr(a != FlagA.B, "(!= (sig a) (const 4'd4))")
-        self.assertRepr(a | c, "FlagView(FlagA, (| (sig a) (sig c)))")
-        self.assertRepr(a & c, "FlagView(FlagA, (& (sig a) (sig c)))")
-        self.assertRepr(a ^ c, "FlagView(FlagA, (^ (sig a) (sig c)))")
-        self.assertRepr(~a, "FlagView(FlagA, (& (~ (sig a)) (const 3'd5)))")
-        self.assertRepr(a | FlagA.B, "FlagView(FlagA, (| (sig a) (const 4'd4)))")
+        self.assertRepr(a | c, f"FlagView({FlagA.__qualname__}, (| (sig a) (sig c)))")
+        self.assertRepr(a & c, f"FlagView({FlagA.__qualname__}, (& (sig a) (sig c)))")
+        self.assertRepr(a ^ c, f"FlagView({FlagA.__qualname__}, (^ (sig a) (sig c)))")
+        self.assertRepr(~a, f"FlagView({FlagA.__qualname__}, (& (~ (sig a)) (const 3'd5)))")
+        self.assertRepr(a | FlagA.B, f"FlagView({FlagA.__qualname__}, (| (sig a) (const 4'd4)))")
         if sys.version_info >= (3, 11):
             class FlagC(Flag, shape=unsigned(4), boundary=py_enum.KEEP):
                 A = 1
                 B = 4
             e = Signal(FlagC)
-            self.assertRepr(~e, "FlagView(FlagC, (~ (sig e)))")
+            self.assertRepr(~e, f"FlagView({FlagC.__qualname__}, (~ (sig e)))")
 
     def test_enum_view_wrong(self):
         class EnumA(Enum, shape=signed(4)):
@@ -327,6 +327,4 @@ class EnumTestCase(FHDLTestCase):
             B = 1
 
         sig = Signal(EnumA)
-        self.assertRepr(EnumA.format(sig, ""), """
-        (format-enum (sig sig) 'EnumA' (0 'A') (1 'B'))
-        """)
+        self.assertRepr(EnumA.format(sig, ""), f"(format-enum (sig sig) '{EnumA.__qualname__}' (0 'A') (1 'B'))")
diff --git a/tests/test_lib_wiring.py b/tests/test_lib_wiring.py
index d4b7d8c..79f96e2 100644
--- a/tests/test_lib_wiring.py
+++ b/tests/test_lib_wiring.py
@@ -635,7 +635,7 @@ class PureInterfaceTestCase(unittest.TestCase):
         class CustomInterface(PureInterface):
             pass
         intf = CustomInterface(Signature({}), path=())
-        self.assertRegex(repr(intf), r"^<CustomInterface: .+?>$")
+        self.assertRegex(repr(intf), r"^<.+\.CustomInterface: .+?>$")
 
 
 class FlippedInterfaceTestCase(unittest.TestCase):