Repository: sopel-irc/sopel

Issue:
backends: try only utf8 when UTF8ONLY
### Description
When UTF8ONLY is active, raise a decoding error if the message fails to decode instead of attempting other encodings.

As far as I can tell, Sopel never sends non-utf8 data unless someone's poking at the backend directly, so no other changes should be necessary to consider UTF8ONLY supported. (Actually, even this doesn't seem mandatory, just sensible)

### Checklist
- [x] I have read [CONTRIBUTING.md](https://github.com/sopel-irc/sopel/blob/master/CONTRIBUTING.md)
- [x] I can and do license this contribution under the EFLv2
- [x] No issues are reported by `make qa` (runs `make quality` and `make test`)
- [x] I have tested the functionality of the things this change touches



Discussion:
Was trying to add tests and... Windows-1252 is a superset of ISO/IEC 8859-1. I'm removing the latter.
Added, but more for coverage than because it matters =p

Resolving patch:
diff --git a/sopel/coretasks.py b/sopel/coretasks.py
--- a/sopel/coretasks.py
+++ b/sopel/coretasks.py
@@ -331,6 +331,10 @@ def handle_isupport(bot, trigger):
 
     bot._isupport = bot._isupport.apply(**parameters)
 
+    # update backend's utf8only setting
+    if 'UTF8ONLY' in bot.isupport:
+        bot.backend.utf8only = True
+
     # update bot's mode parser
     if 'CHANMODES' in bot.isupport:
         bot.modeparser.chanmodes = bot.isupport.CHANMODES
diff --git a/sopel/irc/abstract_backends.py b/sopel/irc/abstract_backends.py
--- a/sopel/irc/abstract_backends.py
+++ b/sopel/irc/abstract_backends.py
@@ -24,6 +24,9 @@ class AbstractIRCBackend(abc.ABC):
     backend implementation will not function correctly.
     """
     def __init__(self, bot: AbstractBot):
+        self.utf8only: bool = False
+        """Whether we are operating in utf8-only mode."""
+
         self.bot: AbstractBot = bot
 
     @abc.abstractmethod
@@ -69,7 +72,7 @@ def decode_line(self, line: bytes) -> str:
             data = str(line, encoding='utf-8')
         except UnicodeDecodeError as e:
             # ...unless the server announces UTF8ONLY
-            if "UTF8ONLY" in self.isupport:
+            if self.utf8only:
                 raise e
             # not Unicode; let's try CP-1252
             try:


Test patch:
diff --git a/test/irc/test_irc_abstract_backends.py b/test/irc/test_irc_abstract_backends.py
--- a/test/irc/test_irc_abstract_backends.py
+++ b/test/irc/test_irc_abstract_backends.py
@@ -3,7 +3,6 @@
 
 import pytest
 
-from sopel.irc.isupport import ISupport
 from sopel.tests.mocks import MockIRCBackend
 
 
@@ -305,7 +304,6 @@ def test_send_notice_safe():
 def test_decode_line_utf8():
     bot = BotCollector()
     backend = MockIRCBackend(bot)
-    backend.isupport = ISupport()
 
     test = "PRIVMSG #sopel :Hello, Martín!"
     assert backend.decode_line(test.encode("utf-8")) == test
@@ -314,7 +312,7 @@ def test_decode_line_utf8():
 def test_decode_line_utf8only():
     bot = BotCollector()
     backend = MockIRCBackend(bot)
-    backend.isupport = ISupport(UTF8ONLY=None)
+    backend.utf8only = True
 
     test = "PRIVMSG #sopel :Hello, Martín!"
     with pytest.raises(UnicodeDecodeError):
@@ -324,7 +322,6 @@ def test_decode_line_utf8only():
 def test_decode_line_nonsense():
     bot = BotCollector()
     backend = MockIRCBackend(bot)
-    backend.isupport = ISupport()
 
     with pytest.raises(ValueError):
         backend.decode_line(bytes(range(256)))
@@ -333,7 +330,6 @@ def test_decode_line_nonsense():
 def test_decode_line_cp1252():
     bot = BotCollector()
     backend = MockIRCBackend(bot)
-    backend.isupport = ISupport()
 
     test = "PRIVMSG #sopel :Hello, Martín!"
     assert backend.decode_line(test.encode("cp1252")) == test
diff --git a/test/test_coretasks.py b/test/test_coretasks.py
--- a/test/test_coretasks.py
+++ b/test/test_coretasks.py
@@ -471,6 +471,17 @@ def test_handle_isupport_namesx_with_multi_prefix(mockbot):
     )
 
 
+def test_handle_isupport_utf8only(mockbot):
+    assert not mockbot.backend.utf8only
+
+    mockbot.on_message(
+        ":irc.example.com 005 Sopel UTF8ONLY :are supported by this server"
+    )
+
+    assert "UTF8ONLY" in mockbot.isupport
+    assert mockbot.backend.utf8only
+
+
 def test_handle_rpl_myinfo(mockbot):
     """Test handling RPL_MYINFO events."""
     assert not hasattr(mockbot, 'myinfo'), (