Skip to content

Commit f9648ba

Browse files
committed
gh-155292: Skip updating unicodedata with mismatched interpreter
mkstringprep uses things like str.lower(), so it generates the wrong result if run in an interpreter with a different Unicode data version than the target. This means that updating the Unicode version is a two-step process: run makeunicodedata.py, then compile, then run mkstringprep.py. The two steps can (and should) be combined when re-running regen-unicodedata to verify that the data is up to date. The GH-155292 fix only considered that case. Change makeunicodedata.py to only run mkstringprep.py when the current interpreter is up to it. Otherwise, show a reminder. As an extra complication, download the input (RFC 3454) in the "first step", since an out-of-date stringprep.py's freshness assertion may prevent downloads.
1 parent 3b56438 commit f9648ba

2 files changed

Lines changed: 30 additions & 15 deletions

File tree

Tools/unicode/makeunicodedata.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,10 +816,31 @@ def makeunicodename(unicode, trace):
816816
def makestringprep():
817817
FILE = "Lib/stringprep.py"
818818

819+
RFC_LOCAL = os.path.join(DATA_DIR, "rfc3454.txt")
820+
RFC_URL = "https://www.rfc-editor.org/rfc/rfc3454.txt"
821+
819822
print("--- Preparing", FILE, "...")
820823

824+
# mkstringprep expects a local copy of RFC 3454. Download it now.
825+
# (stringprep is used for URL handling, and if it's not matched
826+
# with the compiled unicodedata, downloads would fail.)
827+
if not os.path.exists(RFC_LOCAL):
828+
download_data(RFC_LOCAL, RFC_URL)
829+
821830
MKSTRINGPREP = "Tools/unicode/mkstringprep.py"
822831

832+
# mkstringprep needs to be run with a Python version that has "its"
833+
# unicode data, since it uses str.lower() and similar.
834+
import unicodedata
835+
if unicodedata.unidata_version != UNIDATA_VERSION:
836+
print()
837+
print("!! Skipping mkstringprep -- mimatched Unicode version !!")
838+
print()
839+
print("Please compile CPython with the updated Unicode database,")
840+
print("then use that interpreter to run:")
841+
print(f" python {MKSTRINGPREP} > {FILE}")
842+
return
843+
823844
with open(FILE, "w") as f:
824845
f.truncate()
825846
subprocess.check_call([sys.executable, MKSTRINGPREP], stdout=f)
@@ -934,21 +955,26 @@ class Difference(Exception):pass
934955
def open_data(template, version):
935956
local = os.path.join(DATA_DIR, template % ('-'+version,))
936957
if not os.path.exists(local):
937-
import urllib.request
938958
if version == '3.2.0':
939959
# irregular url structure
940960
url = ('https://www.unicode.org/Public/3.2-Update/'+template) % ('-'+version,)
941961
else:
942962
url = ('https://www.unicode.org/Public/%s/ucd/'+template) % (version, '')
943-
os.makedirs(os.path.dirname(local), exist_ok=True)
944-
urllib.request.urlretrieve(url, filename=local)
963+
download_data(local, url)
945964
if local.endswith('.txt'):
946965
return open(local, encoding='utf-8')
947966
else:
948967
# Unihan.zip
949968
return open(local, 'rb')
950969

951970

971+
def download_data(local, url):
972+
import urllib.request
973+
os.makedirs(os.path.dirname(local), exist_ok=True)
974+
print(f'Downloading {url} to {local}')
975+
urllib.request.urlretrieve(url, filename=local)
976+
977+
952978
def expand_range(char_range: str) -> Iterator[int]:
953979
'''
954980
Parses ranges of code points, as described in UAX #44:

Tools/unicode/mkstringprep.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
import unicodedata as unicodedata_current
44
from unicodedata import ucd_3_2_0 as unicodedata_320
55

6-
FILENAME = "Tools/unicode/data/rfc3454.txt"
7-
URL = "https://www.rfc-editor.org/rfc/rfc3454.txt"
8-
96
def gen_category(cats):
107
for i in range(0, 0x110000):
118
if unicodedata_320.category(chr(i)) in cats:
@@ -52,15 +49,7 @@ def compact_set(l):
5249

5350
############## Read the tables in the RFC #######################
5451

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:
52+
with open("Tools/unicode/data/rfc3454.txt", encoding='utf-8') as data_file:
6453
data = data_file.readlines()
6554

6655
tables = []

0 commit comments

Comments
 (0)