| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4fda068 commit d397a49
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1547,6 +1547,15 @@ def test_builtin_encode(self): | |||
| 1547 | 1547 | self.assertEqual("pyth\xf6n.org".encode("idna"), b"xn--pythn-mua.org") | |
| 1548 | 1548 | self.assertEqual("pyth\xf6n.org.".encode("idna"), b"xn--pythn-mua.org.") | |
| 1549 | 1549 | ||
| 1550 | + @support.subTests(['unicode', 'encoded'], [ | ||
| 1551 | + ('\N{CHEROKEE LETTER A}\N{CHEROKEE LETTER A}', b"xn--58da"), | ||
| 1552 | + ('\N{GEORGIAN CAPITAL LETTER AN}.', b"xn--7md."), | ||
| 1553 | + ('\N{CYRILLIC LETTER PALOCHKA}.example', b"xn--d5a.example"), | ||
| 1554 | + ('\N{ROMAN NUMERAL REVERSED ONE HUNDRED}.example.', b"xn--q5g.example."), | ||
| 1555 | + ]) | ||
| 1556 | + def test_new_unicode_case_folding(self, unicode, encoded): | ||
| 1557 | + self.assertEqual(unicode.encode("idna"), encoded) | ||
| 1558 | + | ||
| 1550 | 1559 | def test_builtin_decode_length_limit(self): | |
| 1551 | 1560 | with self.assertRaisesRegex(UnicodeError, "too long"): | |
| 1552 | 1561 | (b"xn--016c"+b"a"*1100).decode("idna") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -69,6 +69,7 @@ class UnicodeDatabaseTest(unittest.TestCase): | |||
| 69 | 69 | db = unicodedata | |
| 70 | 70 | ||
| 71 | 71 | class UnicodeFunctionsTest(UnicodeDatabaseTest): | |
| 72 | + old = False | ||
| 72 | 73 | ||
| 73 | 74 | # Update this if the database changes. Make sure to do a full rebuild | |
| 74 | 75 | # (e.g. 'make distclean && make') to get the correct checksum. | |
@@ -94,7 +95,8 @@ def test_function_checksum(self): | |||
| 94 | 95 | ] | |
| 95 | 96 | h.update(''.join(data).encode("ascii")) | |
| 96 | 97 | result = h.hexdigest() | |
| 97 | - self.assertEqual(result, self.expectedchecksum) | ||
| 98 | + if not self.old: | ||
| 99 | + self.assertEqual(result, self.expectedchecksum) | ||
| 98 | 100 | ||
| 99 | 101 | @requires_resource('cpu') | |
| 100 | 102 | def test_name_inverse_lookup(self): | |
@@ -120,9 +122,13 @@ def test_numeric(self): | |||
| 120 | 122 | self.assertEqual(self.db.numeric('9'), 9) | |
| 121 | 123 | self.assertEqual(self.db.numeric('\u215b'), 0.125) | |
| 122 | 124 | self.assertEqual(self.db.numeric('\u2468'), 9.0) | |
| 123 | - self.assertEqual(self.db.numeric('\ua627'), 7.0) | ||
| 125 | + # New in 5.1.0 | ||
| 126 | + self.assertEqual(self.db.numeric('\ua627', None), | ||
| 127 | + None if self.old else 7.0) | ||
| 124 | 128 | self.assertEqual(self.db.numeric('\U00020000', None), None) | |
| 125 | - self.assertEqual(self.db.numeric('\U0001012A'), 9000) | ||
| 129 | + # New in 4.1.0 | ||
| 130 | + self.assertEqual(self.db.numeric('\U0001012A', None), | ||
| 131 | + None if self.old else 9000) | ||
| 126 | 132 | ||
| 127 | 133 | self.assertRaises(TypeError, self.db.numeric) | |
| 128 | 134 | self.assertRaises(TypeError, self.db.numeric, 'xx') | |
@@ -145,7 +151,8 @@ def test_category(self): | |||
| 145 | 151 | self.assertEqual(self.db.category('a'), 'Ll') | |
| 146 | 152 | self.assertEqual(self.db.category('A'), 'Lu') | |
| 147 | 153 | self.assertEqual(self.db.category('\U00020000'), 'Lo') | |
| 148 | - self.assertEqual(self.db.category('\U0001012A'), 'No') | ||
| 154 | + self.assertEqual(self.db.category('\U0001012A'), | ||
| 155 | + 'Cn' if self.old else 'No') | ||
| 149 | 156 | ||
| 150 | 157 | self.assertRaises(TypeError, self.db.category) | |
| 151 | 158 | self.assertRaises(TypeError, self.db.category, 'xx') | |
@@ -159,6 +166,15 @@ def test_bidirectional(self): | |||
| 159 | 166 | self.assertRaises(TypeError, self.db.bidirectional) | |
| 160 | 167 | self.assertRaises(TypeError, self.db.bidirectional, 'xx') | |
| 161 | 168 | ||
| 169 | + def test_bidirectional_unassigned(self): | ||
| 170 | + self.assertEqual(self.db.bidirectional('\u0378'), '') | ||
| 171 | + self.assertEqual(self.db.bidirectional('\u077F'), '' if self.old else 'AL') | ||
| 172 | + self.assertEqual(self.db.bidirectional('\u20CF'), '') | ||
| 173 | + self.assertEqual(self.db.bidirectional('\u0590'), '') | ||
| 174 | + self.assertEqual(self.db.bidirectional('\uFFFF'), '') | ||
| 175 | + self.assertEqual(self.db.bidirectional('\U0001FFFE'), '') | ||
| 176 | + self.assertEqual(self.db.bidirectional('\U00010D01'), '' if self.old else 'AL') | ||
| 177 | + | ||
| 162 | 178 | def test_decomposition(self): | |
| 163 | 179 | self.assertEqual(self.db.decomposition('\uFFFE'),'') | |
| 164 | 180 | self.assertEqual(self.db.decomposition('\u00bc'), '<fraction> 0031 2044 0034') | |
@@ -257,8 +273,14 @@ def test_east_asian_width(self): | |||
| 257 | 273 | self.assertEqual(eaw('\U00020000'), 'W') | |
| 258 | 274 | ||
| 259 | 275 | def test_east_asian_width_9_0_changes(self): | |
| 260 | - self.assertEqual(self.db.ucd_3_2_0.east_asian_width('\u231a'), 'N') | ||
| 261 | - self.assertEqual(self.db.east_asian_width('\u231a'), 'W') | ||
| 276 | + self.assertEqual(self.db.east_asian_width('\u231a'), | ||
| 277 | + 'N' if self.old else 'W') | ||
| 278 | + | ||
| 279 | + | ||
| 280 | + class Unicode_3_2_0_FunctionsTest(UnicodeFunctionsTest): | ||
| 281 | + db = unicodedata.ucd_3_2_0 | ||
| 282 | + old = True | ||
| 283 | + | ||
| 262 | 284 | ||
| 263 | 285 | class UnicodeMiscTest(UnicodeDatabaseTest): | |
| 264 | 286 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Change the :mod:`stringprep` module and :mod:`encodings.idna` codec to not | ||
| 2 | + consider Unicode codepoint attributes beyond those defined in :rfc:`3454`. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,14 +28,15 @@ | |||
| 28 | 28 | ||
| 29 | 29 | import dataclasses | |
| 30 | 30 | import os | |
| 31 | + import subprocess | ||
| 31 | 32 | import sys | |
| 32 | 33 | import zipfile | |
| 33 | 34 | ||
| 34 | 35 | from functools import partial | |
| 35 | 36 | from textwrap import dedent | |
| 36 | 37 | from typing import Iterator, List, Optional, Set, Tuple | |
| 37 | 38 | ||
| 38 | - SCRIPT = sys.argv[0] | ||
| 39 | + SCRIPT = os.path.normpath(sys.argv[0]) | ||
| 39 | 40 | VERSION = "3.3" | |
| 40 | 41 | ||
| 41 | 42 | # The Unicode Database | |
@@ -128,6 +129,7 @@ def maketables(trace=0): | |||
| 128 | 129 | makeunicodename(unicode, trace) | |
| 129 | 130 | makeunicodedata(unicode, trace) | |
| 130 | 131 | makeunicodetype(unicode, trace) | |
| 132 | + makestringprep() | ||
| 131 | 133 | ||
| 132 | 134 | ||
| 133 | 135 | # -------------------------------------------------------------------- | |
@@ -792,6 +794,19 @@ def word_key(a): | |||
| 792 | 794 | fprint('};') | |
| 793 | 795 | ||
| 794 | 796 | ||
| 797 | + | ||
| 798 | + def makestringprep(): | ||
| 799 | + FILE = "Lib/stringprep.py" | ||
| 800 | + | ||
| 801 | + print("--- Preparing", FILE, "...") | ||
| 802 | + | ||
| 803 | + MKSTRINGPREP = "Tools/unicode/mkstringprep.py" | ||
| 804 | + | ||
| 805 | + with open(FILE, "w") as f: | ||
| 806 | + f.truncate() | ||
| 807 | + subprocess.check_call([sys.executable, MKSTRINGPREP], stdout=f) | ||
| 808 | + | ||
| 809 | + | ||
| 795 | 810 | def merge_old_version(version, new, old): | |
| 796 | 811 | # Changes to exclusion file not implemented yet | |
| 797 | 812 | if old.exclusions != new.exclusions: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,15 +1,20 @@ | |||
| 1 | 1 | import re | |
| 2 | - from unicodedata import ucd_3_2_0 as unicodedata | ||
| 2 | + import os | ||
| 3 | + import unicodedata as unicodedata_current | ||
| 4 | + from unicodedata import ucd_3_2_0 as unicodedata_320 | ||
| 5 | + | ||
| 6 | + FILENAME = "Tools/unicode/data/rfc3454.txt" | ||
| 7 | + URL = "https://www.rfc-editor.org/rfc/rfc3454.txt" | ||
| 3 | 8 | ||
| 4 | 9 | def gen_category(cats): | |
| 5 | 10 | for i in range(0, 0x110000): | |
| 6 | - if unicodedata.category(chr(i)) in cats: | ||
| 7 | - yield(i) | ||
| 11 | + if unicodedata_320.category(chr(i)) in cats: | ||
| 12 | + yield i | ||
| 8 | 13 | ||
| 9 | 14 | def gen_bidirectional(cats): | |
| 10 | 15 | for i in range(0, 0x110000): | |
| 11 | - if unicodedata.bidirectional(chr(i)) in cats: | ||
| 12 | - yield(i) | ||
| 16 | + if unicodedata_320.bidirectional(chr(i)) in cats: | ||
| 17 | + yield i | ||
| 13 | 18 | ||
| 14 | 19 | def compact_set(l): | |
| 15 | 20 | single = [] | |
@@ -47,8 +52,16 @@ def compact_set(l): | |||
| 47 | 52 | ||
| 48 | 53 | ############## Read the tables in the RFC ####################### | |
| 49 | 54 | ||
| 50 | - with open("rfc3454.txt") as f: | ||
| 51 | - data = f.readlines() | ||
| 55 | + try: | ||
| 56 | + data_file = open(FILENAME, encoding='utf-8') | ||
| 57 | + except FileNotFoundError: | ||
| 58 | + import urllib.request | ||
| 59 | + os.makedirs(os.path.dirname(FILENAME), exist_ok=True) | ||
| 60 | + urllib.request.urlretrieve(URL, filename=FILENAME) | ||
| 61 | + data_file = open(FILENAME, encoding='utf-8') | ||
| 62 | + | ||
| 63 | + with data_file: | ||
| 64 | + data = data_file.readlines() | ||
| 52 | 65 | ||
| 53 | 66 | tables = [] | |
| 54 | 67 | curname = None | |
@@ -116,10 +129,18 @@ def compact_set(l): | |||
| 116 | 129 | and mappings, for which a mapping function is provided. | |
| 117 | 130 | \"\"\" | |
| 118 | 131 | ||
| 119 | - from unicodedata import ucd_3_2_0 as unicodedata | ||
| 132 | + # This check asserts that mkstringprep.py has been run | ||
| 133 | + # when unicodedata is modified to ensure conformant behavior. | ||
| 134 | + import unicodedata | ||
| 135 | + """) | ||
| 136 | + | ||
| 137 | + print("assert unicodedata.unidata_version == %r" % (unicodedata_current.unidata_version,)) | ||
| 138 | + | ||
| 139 | + print(""" | ||
| 140 | + from unicodedata import ucd_3_2_0 as unicodedata_320 | ||
| 120 | 141 | """) | |
| 121 | 142 | ||
| 122 | - print("assert unicodedata.unidata_version == %r" % (unicodedata.unidata_version,)) | ||
| 143 | + print("assert unicodedata_320.unidata_version == %r" % (unicodedata_320.unidata_version,)) | ||
| 123 | 144 | ||
| 124 | 145 | # A.1 is the table of unassigned characters | |
| 125 | 146 | # XXX Plane 15 PUA is listed as unassigned in Python. | |
@@ -139,7 +160,7 @@ def compact_set(l): | |||
| 139 | 160 | ||
| 140 | 161 | print(""" | |
| 141 | 162 | def in_table_a1(code): | |
| 142 | - if unicodedata.category(code) != 'Cn': return False | ||
| 163 | + if unicodedata_320.category(code) != 'Cn': return False | ||
| 143 | 164 | c = ord(code) | |
| 144 | 165 | if 0xFDD0 <= c < 0xFDF0: return False | |
| 145 | 166 | return (c & 0xFFFF) not in (0xFFFE, 0xFFFF) | |
@@ -172,21 +193,33 @@ def in_table_b1(code): | |||
| 172 | 193 | ||
| 173 | 194 | # B.3 is mostly Python's .lower, except for a number | |
| 174 | 195 | # of special cases, e.g. considering canonical forms. | |
| 196 | + # To enforce Unicode 3.2.0 behavior of .lower instead of | ||
| 197 | + # whatever Unicode version is included with Python we | ||
| 198 | + # add unassigned or newly case-folding codepoints to | ||
| 199 | + # the exception map, too. | ||
| 175 | 200 | ||
| 176 | 201 | b3_exceptions = {} | |
| 177 | 202 | ||
| 178 | 203 | for k,v in table_b2.items(): | |
| 179 | 204 | if list(map(ord, chr(k).lower())) != v: | |
| 180 | 205 | b3_exceptions[k] = "".join(map(chr,v)) | |
| 206 | + for cp in range(0x110000): | ||
| 207 | + ch = chr(cp) | ||
| 208 | + # Assigned in current Unicode version | ||
| 209 | + # and supports case folding, but not | ||
| 210 | + # explicitly in B.2 or B.3 tables. | ||
| 211 | + if (unicodedata_current.category(ch) != "Cn" | ||
| 212 | + and ch.lower() != ch | ||
| 213 | + and cp not in table_b2 | ||
| 214 | + and cp not in table_b3): | ||
| 215 | + b3_exceptions[cp] = ch # Identity. | ||
| 181 | 216 | ||
| 182 | 217 | b3 = sorted(b3_exceptions.items()) | |
| 183 | 218 | ||
| 184 | 219 | print(""" | |
| 185 | 220 | b3_exceptions = {""") | |
| 186 | 221 | for i, kv in enumerate(b3): | |
| 187 | - print("0x%x:%a," % kv, end=' ') | ||
| 188 | - if i % 4 == 3: | ||
| 189 | - print() | ||
| 222 | + print("0x%x:%a," % kv, end='\n' if i % 4 == 3 else ' ') | ||
| 190 | 223 | print("}") | |
| 191 | 224 | ||
| 192 | 225 | print(""" | |
@@ -207,9 +240,9 @@ def map_table_b3(code): | |||
| 207 | 240 | ||
| 208 | 241 | def map_table_b2(a): | |
| 209 | 242 | al = map_table_b3(a) | |
| 210 | - b = unicodedata.normalize("NFKC", al) | ||
| 243 | + b = unicodedata_320.normalize("NFKC", al) | ||
| 211 | 244 | bl = "".join([map_table_b3(ch) for ch in b]) | |
| 212 | - c = unicodedata.normalize("NFKC", bl) | ||
| 245 | + c = unicodedata_320.normalize("NFKC", bl) | ||
| 213 | 246 | if b != c: | |
| 214 | 247 | return c | |
| 215 | 248 | else: | |
@@ -226,9 +259,9 @@ def map_table_b2(a): | |||
| 226 | 259 | print(""" | |
| 227 | 260 | def map_table_b2(a): | |
| 228 | 261 | al = map_table_b3(a) | |
| 229 | - b = unicodedata.normalize("NFKC", al) | ||
| 262 | + b = unicodedata_320.normalize("NFKC", al) | ||
| 230 | 263 | bl = "".join([map_table_b3(ch) for ch in b]) | |
| 231 | - c = unicodedata.normalize("NFKC", bl) | ||
| 264 | + c = unicodedata_320.normalize("NFKC", bl) | ||
| 232 | 265 | if b != c: | |
| 233 | 266 | return c | |
| 234 | 267 | else: | |
@@ -251,16 +284,16 @@ def in_table_c11(code): | |||
| 251 | 284 | del tables[0] | |
| 252 | 285 | assert name == "C.1.2" | |
| 253 | 286 | ||
| 254 | - # table = set(table.keys()) | ||
| 255 | - # Zs = set(gen_category(["Zs"])) - {0x20} | ||
| 256 | - # assert Zs == table | ||
| 287 | + table = set(table.keys()) | ||
| 288 | + Zs = set(gen_category(["Zs"])) - {0x20} | ||
| 289 | + assert Zs == table | ||
| 257 | 290 | ||
| 258 | 291 | print(""" | |
| 259 | 292 | def in_table_c12(code): | |
| 260 | - return unicodedata.category(code) == "Zs" and code != " " | ||
| 293 | + return unicodedata_320.category(code) == "Zs" and code != " " | ||
| 261 | 294 | ||
| 262 | 295 | def in_table_c11_c12(code): | |
| 263 | - return unicodedata.category(code) == "Zs" | ||
| 296 | + return unicodedata_320.category(code) == "Zs" | ||
| 264 | 297 | """) | |
| 265 | 298 | ||
| 266 | 299 | # C.2.1 ASCII control characters | |
@@ -275,7 +308,7 @@ def in_table_c11_c12(code): | |||
| 275 | 308 | ||
| 276 | 309 | print(""" | |
| 277 | 310 | def in_table_c21(code): | |
| 278 | - return ord(code) < 128 and unicodedata.category(code) == "Cc" | ||
| 311 | + return ord(code) < 128 and unicodedata_320.category(code) == "Cc" | ||
| 279 | 312 | """) | |
| 280 | 313 | ||
| 281 | 314 | # C.2.2 Non-ASCII control characters. It also includes | |
@@ -295,11 +328,11 @@ def in_table_c21(code): | |||
| 295 | 328 | def in_table_c22(code): | |
| 296 | 329 | c = ord(code) | |
| 297 | 330 | if c < 128: return False | |
| 298 | - if unicodedata.category(code) == "Cc": return True | ||
| 331 | + if unicodedata_320.category(code) == "Cc": return True | ||
| 299 | 332 | return c in c22_specials | |
| 300 | 333 | ||
| 301 | 334 | def in_table_c21_c22(code): | |
| 302 | - return unicodedata.category(code) == "Cc" or \\ | ||
| 335 | + return unicodedata_320.category(code) == "Cc" or \\ | ||
| 303 | 336 | ord(code) in c22_specials | |
| 304 | 337 | """) | |
| 305 | 338 | ||
@@ -313,7 +346,7 @@ def in_table_c21_c22(code): | |||
| 313 | 346 | ||
| 314 | 347 | print(""" | |
| 315 | 348 | def in_table_c3(code): | |
| 316 | - return unicodedata.category(code) == "Co" | ||
| 349 | + return unicodedata_320.category(code) == "Co" | ||
| 317 | 350 | """) | |
| 318 | 351 | ||
| 319 | 352 | # C.4 Non-character code points, xFFFE, xFFFF | |
@@ -346,7 +379,7 @@ def in_table_c4(code): | |||
| 346 | 379 | ||
| 347 | 380 | print(""" | |
| 348 | 381 | def in_table_c5(code): | |
| 349 | - return unicodedata.category(code) == "Cs" | ||
| 382 | + return unicodedata_320.category(code) == "Cs" | ||
| 350 | 383 | """) | |
| 351 | 384 | ||
| 352 | 385 | # C.6 Inappropriate for plain text | |
@@ -411,7 +444,7 @@ def in_table_c9(code): | |||
| 411 | 444 | ||
| 412 | 445 | print(""" | |
| 413 | 446 | def in_table_d1(code): | |
| 414 | - return unicodedata.bidirectional(code) in ("R","AL") | ||
| 447 | + return unicodedata_320.bidirectional(code) in ("R","AL") | ||
| 415 | 448 | """) | |
| 416 | 449 | ||
| 417 | 450 | # D.2 Characters with bidirectional property "L" | |
@@ -424,5 +457,5 @@ def in_table_d1(code): | |||
| 424 | 457 | ||
| 425 | 458 | print(""" | |
| 426 | 459 | def in_table_d2(code): | |
| 427 | - return unicodedata.bidirectional(code) == "L" | ||
| 428 | - """) | ||
| 460 | + return unicodedata_320.bidirectional(code) == "L" | ||
| 461 | + """, end="") | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments