1- import re , sys
2- from unicodedata import ucd_3_2_0 as unicodedata
1+ import re , sys , os
2+ import unicodedata as unicodedata_current
3+ from unicodedata import ucd_3_2_0 as unicodedata_320
34
45if sys .maxunicode == 65535 :
56 raise RuntimeError ("need UCS-4 Python" )
67
8+ FILENAME = "Tools/unicode/data/rfc3454.txt"
9+ URL = "https://www.rfc-editor.org/rfc/rfc3454.txt"
10+
711def gen_category (cats ):
812 for i in range (0 , 0x110000 ):
9- if unicodedata .category (chr (i )) in cats :
10- yield ( i )
13+ if unicodedata_320 .category (chr (i )) in cats :
14+ yield i
1115
1216def gen_bidirectional (cats ):
1317 for i in range (0 , 0x110000 ):
14- if unicodedata .bidirectional (chr (i )) in cats :
15- yield ( i )
18+ if unicodedata_320 .bidirectional (chr (i )) in cats :
19+ yield i
1620
1721def compact_set (l ):
1822 single = []
@@ -50,8 +54,16 @@ def compact_set(l):
5054
5155############## Read the tables in the RFC #######################
5256
53- with open ("rfc3454.txt" ) as f :
54- data = f .readlines ()
57+ try :
58+ data_file = open (FILENAME , encoding = 'utf-8' )
59+ except FileNotFoundError :
60+ import urllib .request
61+ os .makedirs (os .path .dirname (FILENAME ), exist_ok = True )
62+ urllib .request .urlretrieve (URL , filename = FILENAME )
63+ data_file = open (FILENAME , encoding = 'utf-8' )
64+
65+ with data_file :
66+ data = data_file .readlines ()
5567
5668tables = []
5769curname = None
@@ -119,10 +131,18 @@ def compact_set(l):
119131and mappings, for which a mapping function is provided.
120132\" \" \"
121133
122- from unicodedata import ucd_3_2_0 as unicodedata
134+ # This check asserts that mkstringprep.py has been run
135+ # when unicodedata is modified to ensure conformant behavior.
136+ import unicodedata
137+ """ )
138+
139+ print ("assert unicodedata.unidata_version == %r" % (unicodedata_current .unidata_version ,))
140+
141+ print ("""
142+ from unicodedata import ucd_3_2_0 as unicodedata_320
123143""" )
124144
125- print ("assert unicodedata .unidata_version == %r" % (unicodedata .unidata_version ,))
145+ print ("assert unicodedata_320 .unidata_version == %r" % (unicodedata_320 .unidata_version ,))
126146
127147# A.1 is the table of unassigned characters
128148# XXX Plane 15 PUA is listed as unassigned in Python.
@@ -142,7 +162,7 @@ def compact_set(l):
142162
143163print ("""
144164def in_table_a1(code):
145- if unicodedata .category(code) != 'Cn': return False
165+ if unicodedata_320 .category(code) != 'Cn': return False
146166 c = ord(code)
147167 if 0xFDD0 <= c < 0xFDF0: return False
148168 return (c & 0xFFFF) not in (0xFFFE, 0xFFFF)
@@ -175,21 +195,33 @@ def in_table_b1(code):
175195
176196# B.3 is mostly Python's .lower, except for a number
177197# of special cases, e.g. considering canonical forms.
198+ # To enforce Unicode 3.2.0 behavior of .lower instead of
199+ # whatever Unicode version is included with Python we
200+ # add unassigned or newly case-folding codepoints to
201+ # the exception map, too.
178202
179203b3_exceptions = {}
180204
181205for k ,v in table_b2 .items ():
182206 if list (map (ord , chr (k ).lower ())) != v :
183207 b3_exceptions [k ] = "" .join (map (chr ,v ))
208+ for cp in range (0x110000 ):
209+ ch = chr (cp )
210+ # Assigned in current Unicode version
211+ # and supports case folding, but not
212+ # explicitly in B.2 or B.3 tables.
213+ if (unicodedata_current .category (ch ) != "Cn"
214+ and ch .lower () != ch
215+ and cp not in table_b2
216+ and cp not in table_b3 ):
217+ b3_exceptions [cp ] = ch # Identity.
184218
185219b3 = sorted (b3_exceptions .items ())
186220
187221print ("""
188222b3_exceptions = {""" )
189223for i , kv in enumerate (b3 ):
190- print ("0x%x:%a," % kv , end = ' ' )
191- if i % 4 == 3 :
192- print ()
224+ print ("0x%x:%a," % kv , end = '\n ' if i % 4 == 3 else ' ' )
193225print ("}" )
194226
195227print ("""
@@ -210,9 +242,9 @@ def map_table_b3(code):
210242
211243def map_table_b2 (a ):
212244 al = map_table_b3 (a )
213- b = unicodedata .normalize ("NFKC" , al )
245+ b = unicodedata_320 .normalize ("NFKC" , al )
214246 bl = "" .join ([map_table_b3 (ch ) for ch in b ])
215- c = unicodedata .normalize ("NFKC" , bl )
247+ c = unicodedata_320 .normalize ("NFKC" , bl )
216248 if b != c :
217249 return c
218250 else :
@@ -229,9 +261,9 @@ def map_table_b2(a):
229261print ("""
230262def map_table_b2(a):
231263 al = map_table_b3(a)
232- b = unicodedata .normalize("NFKC", al)
264+ b = unicodedata_320 .normalize("NFKC", al)
233265 bl = "".join([map_table_b3(ch) for ch in b])
234- c = unicodedata .normalize("NFKC", bl)
266+ c = unicodedata_320 .normalize("NFKC", bl)
235267 if b != c:
236268 return c
237269 else:
@@ -254,16 +286,16 @@ def in_table_c11(code):
254286del tables [0 ]
255287assert name == "C.1.2"
256288
257- # table = set(table.keys())
258- # Zs = set(gen_category(["Zs"])) - {0x20}
259- # assert Zs == table
289+ table = set (table .keys ())
290+ Zs = set (gen_category (["Zs" ])) - {0x20 }
291+ assert Zs == table
260292
261293print ("""
262294def in_table_c12(code):
263- return unicodedata .category(code) == "Zs" and code != " "
295+ return unicodedata_320 .category(code) == "Zs" and code != " "
264296
265297def in_table_c11_c12(code):
266- return unicodedata .category(code) == "Zs"
298+ return unicodedata_320 .category(code) == "Zs"
267299""" )
268300
269301# C.2.1 ASCII control characters
@@ -278,7 +310,7 @@ def in_table_c11_c12(code):
278310
279311print ("""
280312def in_table_c21(code):
281- return ord(code) < 128 and unicodedata .category(code) == "Cc"
313+ return ord(code) < 128 and unicodedata_320 .category(code) == "Cc"
282314""" )
283315
284316# C.2.2 Non-ASCII control characters. It also includes
@@ -298,11 +330,11 @@ def in_table_c21(code):
298330def in_table_c22(code):
299331 c = ord(code)
300332 if c < 128: return False
301- if unicodedata .category(code) == "Cc": return True
333+ if unicodedata_320 .category(code) == "Cc": return True
302334 return c in c22_specials
303335
304336def in_table_c21_c22(code):
305- return unicodedata .category(code) == "Cc" or \\
337+ return unicodedata_320 .category(code) == "Cc" or \\
306338 ord(code) in c22_specials
307339""" )
308340
@@ -316,7 +348,7 @@ def in_table_c21_c22(code):
316348
317349print ("""
318350def in_table_c3(code):
319- return unicodedata .category(code) == "Co"
351+ return unicodedata_320 .category(code) == "Co"
320352""" )
321353
322354# C.4 Non-character code points, xFFFE, xFFFF
@@ -349,7 +381,7 @@ def in_table_c4(code):
349381
350382print ("""
351383def in_table_c5(code):
352- return unicodedata .category(code) == "Cs"
384+ return unicodedata_320 .category(code) == "Cs"
353385""" )
354386
355387# C.6 Inappropriate for plain text
@@ -414,7 +446,7 @@ def in_table_c9(code):
414446
415447print ("""
416448def in_table_d1(code):
417- return unicodedata .bidirectional(code) in ("R","AL")
449+ return unicodedata_320 .bidirectional(code) in ("R","AL")
418450""" )
419451
420452# D.2 Characters with bidirectional property "L"
@@ -427,5 +459,5 @@ def in_table_d1(code):
427459
428460print ("""
429461def in_table_d2(code):
430- return unicodedata .bidirectional(code) == "L"
431- """ )
462+ return unicodedata_320 .bidirectional(code) == "L"
463+ """ , end = "" )
0 commit comments