From: Bob Halley Date: Sat, 7 Feb 2026 15:14:14 +0000 (-0800) Subject: IDNA default improvements X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=fd9c000457f5bd5a15157087d53ff7137d4c94af;p=thirdparty%2Fdnspython.git IDNA default improvements * If idna is available, make IDNA_2008_Practical the default idna_codec. * Add a way to set the default IDNA codec. --- diff --git a/dns/name.py b/dns/name.py index cfdf2d64..e705126d 100644 --- a/dns/name.py +++ b/dns/name.py @@ -316,6 +316,15 @@ IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) IDNA_2008 = IDNA_2008_Practical +if have_idna_2008: + IDNA_DEFAULT = IDNA_2008_Practical +else: + IDNA_DEFAULT = IDNA_2003_Practical + + +def set_default_idna_codec(idna_codec: IDNACodec): + global IDNA_DEFAULT + IDNA_DEFAULT = idna_codec def _validate_labels(labels: tuple[bytes, ...]) -> None: @@ -605,11 +614,7 @@ class Name: dot (denoting the root label) for absolute names. The default is False. *idna_codec* specifies the IDNA encoder/decoder. If None, the - dns.name.IDNA_2003_Practical encoder/decoder is used. - The IDNA_2003_Practical decoder does - not impose any policy, it just decodes punycode, so if you - don't want checking for compliance, you can use this decoder - for IDNA2008 as well. + dns.name.IDNA_DEFAULT encoder/decoder is used. Returns a ``str``. """ @@ -623,7 +628,7 @@ class Name: else: l = self.labels if idna_codec is None: - idna_codec = IDNA_2003_Practical + idna_codec = IDNA_DEFAULT return ".".join([idna_codec.decode(x) for x in l]) def to_digestable(self, origin: "Name | None" = None) -> bytes: @@ -924,7 +929,7 @@ def from_unicode( edigits = 0 total = 0 if idna_codec is None: - idna_codec = IDNA_2003 + idna_codec = IDNA_DEFAULT if text == "@": text = "" if text: diff --git a/tests/test_name.py b/tests/test_name.py index 0addb821..40a409c3 100644 --- a/tests/test_name.py +++ b/tests/test_name.py @@ -804,11 +804,6 @@ class NameTestCase(unittest.TestCase): e = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2003) self.assertEqual(str(e), "xn--knigsgsschen-lcb0w.") - def testFromUnicodeIDNA2003Default(self): - t = "Königsgäßchen" - e = dns.name.from_unicode(t) - self.assertEqual(str(e), "xn--knigsgsschen-lcb0w.") - @unittest.skipUnless( dns.name.have_idna_2008, "Python idna cannot be imported; no IDNA2008" ) @@ -1145,10 +1140,19 @@ class NameTestCase(unittest.TestCase): with self.assertRaises(TypeError): del n.labels - def testUnicodeEscapify(self): - n = dns.name.from_unicode("Königsgäßchen;\ttext") + def testUnicodeEscapify2003(self): + n = dns.name.from_unicode("Königsgäßchen;\ttext", idna_codec=dns.name.IDNA_2003) self.assertEqual(n.to_unicode(), "königsgässchen\\;\\009text.") + @unittest.skipUnless( + dns.name.have_idna_2008, "Python idna cannot be imported; no IDNA2008" + ) + def testUnicodeEscapify2008(self): + def bad(): + dns.name.from_unicode("Königsgäßchen;\ttext", idna_codec=dns.name.IDNA_2008) + + self.assertRaises(dns.name.IDNAException, bad) + def test_pickle(self): n1 = dns.name.from_text("foo.example") p = pickle.dumps(n1) diff --git a/tests/test_rrset.py b/tests/test_rrset.py index 23e33bb9..8d1eb6fa 100644 --- a/tests/test_rrset.py +++ b/tests/test_rrset.py @@ -57,10 +57,20 @@ class RRsetTestCase(unittest.TestCase): def testCodec2003(self): r1 = dns.rrset.from_text_list( - "Königsgäßchen", 30, "in", "ns", ["Königsgäßchen"] + "Königsgäßchen", + 30, + "in", + "ns", + ["Königsgäßchen"], + idna_codec=dns.name.IDNA_2003, ) r2 = dns.rrset.from_text_list( - "xn--knigsgsschen-lcb0w", 30, "in", "ns", ["xn--knigsgsschen-lcb0w"] + "xn--knigsgsschen-lcb0w", + 30, + "in", + "ns", + ["xn--knigsgsschen-lcb0w"], + idna_codec=dns.name.IDNA_2003, ) self.assertEqual(r1, r2)