Repository: twisted/twisted

Issue:
Log formatting of classes/types is broken
**Describe the incorrect behavior you saw**
Attempt to write type in log fails.

**Describe how to cause this behavior**
Emit log message that implies some type formatting.

```
>>> from twisted.logger import eventAsText
>>> eventAsText({"log_format": "{value}", "value": RuntimeError})
"- [-#-] Unable to format event {'log_format': '{value}', 'value': <class 'RuntimeError'>}: object.__format__() takes exactly one argument (0 given)"
```

**Describe the correct behavior you'd like to see**
Type is being formatted just like in `format()` call.

**Additional context**
The regression was introduced in Twisted 24.3.0 (https://github.com/twisted/twisted/pull/12040)
23.10.0 works fine:
```
>>> from twisted.logger import eventAsText
>>> eventAsText({"log_format": "{value}", "value": RuntimeError})
"- [-#-] <class 'RuntimeError'>"
```


Discussion:

Patch:
diff --git a/src/twisted/logger/_format.py b/src/twisted/logger/_format.py
index 59b44c7f72..57d8c3b1e6 100644
--- a/src/twisted/logger/_format.py
+++ b/src/twisted/logger/_format.py
@@ -199,7 +199,7 @@ class PotentialCallWrapper(object):
         self._wrapped = wrapped
 
     def __getattr__(self, name: str) -> object:
-        return keycall(name, self._wrapped.__getattribute__)
+        return keycall(name, lambda name_: getattr(self._wrapped, name_))
 
     def __getitem__(self, name: str) -> object:
         # The sub-object may not be indexable, but if it isn't, that's the
@@ -208,13 +208,13 @@ class PotentialCallWrapper(object):
         return PotentialCallWrapper(value)
 
     def __format__(self, format_spec: str) -> str:
-        return self._wrapped.__format__(format_spec)
+        return format(self._wrapped, format_spec)
 
     def __repr__(self) -> str:
-        return self._wrapped.__repr__()
+        return repr(self._wrapped)
 
     def __str__(self) -> str:
-        return self._wrapped.__str__()
+        return str(self._wrapped)
 
 
 class CallMapping(Mapping[str, Any]):
diff --git a/src/twisted/newsfragments/12323.bugfix b/src/twisted/newsfragments/12323.bugfix
new file mode 100644
index 0000000000..373b75cf58
--- /dev/null
+++ b/src/twisted/newsfragments/12323.bugfix
@@ -0,0 +1,2 @@
+twisted.logger.eventAsText can now format the output having types/classes as input.
+This was a regression introduced in Twisted 24.3.0.
\ No newline at end of file


Test patch:
diff --git a/src/twisted/logger/test/test_format.py b/src/twisted/logger/test/test_format.py
index e16bcc6218..932863fdbe 100644
--- a/src/twisted/logger/test/test_format.py
+++ b/src/twisted/logger/test/test_format.py
@@ -5,7 +5,7 @@
 Test cases for L{twisted.logger._format}.
 """
 
-from typing import AnyStr, Dict, Optional, cast
+from typing import Any, AnyStr, Dict, Optional, cast
 
 try:
     from time import tzset
@@ -713,3 +713,20 @@ class EventAsTextTests(unittest.TestCase):
             eventText,
             "[-\x23info] ABCD",
         )
+
+    def test_eventAsTextTypeAsInput(self) -> None:
+        """
+        C{eventAsText} can handle formats that have classes or types as input.
+        """
+
+        def getText(logFormat: str, **event: Any) -> str:
+            return eventAsText(
+                {"log_format": logFormat, **event},
+                includeTimestamp=False,
+                includeTraceback=False,
+                includeSystem=False,
+            )
+
+        self.assertEqual(str(int), getText("{c}", c=int))
+        self.assertEqual(str(RuntimeError), getText("{c}", c=RuntimeError))
+        self.assertEqual("str", getText("{c.__name__}", c=str))