Skip to content

Commit 3ff89a8

Browse files
committed
Make emitted strings only use u() when they have to
1 parent c23878b commit 3ff89a8

8 files changed

Lines changed: 72 additions & 38 deletions

File tree

python/DumpLocale.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,43 @@
88

99
class DumpLocale {
1010
private static final char SINGLE_QUOTE = 39;
11+
private static final char BACKSLASH = 92;
1112
private static final char[] hexChar = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1213

1314
/* Print a Unicode name suitably escaped */
1415
private static void printName(String name) {
15-
System.out.print("u('");
16-
// Need to escape unicode data
16+
// Need to escape Unicode data if we find it.
17+
boolean seenUnicode = false;
18+
StringBuilder sb = new StringBuilder();
1719
for (int ii=0; ii<name.length(); ii++) {
1820
char c = name.charAt(ii);
1921
if ((c >= 32) && (c < 127)) {
2022
if (c == SINGLE_QUOTE) {
21-
System.out.print("\\'");
23+
sb.append("\\'");
24+
} else if (c == BACKSLASH) {
25+
sb.append("\\\\");
2226
} else {
23-
System.out.print(c);
27+
sb.append(c);
2428
}
2529
} else {
26-
// non-ASCII
27-
System.out.print("\\u");
28-
System.out.print(hexChar[(c >> 12) & 0xF]);
29-
System.out.print(hexChar[(c >> 8) & 0xF]);
30-
System.out.print(hexChar[(c >> 4) & 0xF]);
31-
System.out.print(hexChar[c & 0xF]);
30+
// Non-ASCII. Assume nothing outside of the BMP
31+
seenUnicode = true;
32+
sb.append("\\u");
33+
sb.append(hexChar[(c >> 12) & 0xF]);
34+
sb.append(hexChar[(c >> 8) & 0xF]);
35+
sb.append(hexChar[(c >> 4) & 0xF]);
36+
sb.append(hexChar[c & 0xF]);
3237
}
3338
}
34-
System.out.print("')");
39+
if (seenUnicode) {
40+
System.out.print("u('");
41+
System.out.print(sb.toString());
42+
System.out.print("')");
43+
} else {
44+
System.out.print("'");
45+
System.out.print(sb.toString());
46+
System.out.print("'");
47+
}
3548
}
3649

3750
private static void printProperty(String propName) {

python/buildgeocodingdata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def _stable_dict_repr(strdict):
116116
"""Return a repr() for a dict keyed by a string, in sorted key order"""
117117
lines = []
118118
for key in sorted(strdict.keys()):
119-
lines.append("%s: %s" % (rpr(key), rpr(strdict[key])))
119+
lines.append("'%s': %s" % (key, rpr(strdict[key])))
120120
return "{%s}" % ", ".join(lines)
121121

122122

python/buildmetadatafromxml.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363

6464
# Boilerplate header for individual region data files
6565
_REGION_METADATA_PROLOG = '''"""Auto-generated file, do not edit by hand. %(region)s metadata"""
66-
from %(module)s.util import u
6766
from %(module)s.phonemetadata import NumberFormat, PhoneNumberDesc, PhoneMetadata
6867
'''
6968

python/phonenumbers/geocoder.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
else:
6868
raise
6969

70+
7071
def _may_fall_back_to_english(lang):
7172
# Don't fall back to English if the requested language is among the following:
7273
# - Chinese

python/phonenumbers/phonemetadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def __unicode__(self):
131131
if self.national_prefix_formatting_rule is not None:
132132
result += u(", national_prefix_formatting_rule=%s") % rpr(self.national_prefix_formatting_rule)
133133
if self.national_prefix_optional_when_formatting:
134-
result += u(", national_prefix_optional_when_formatting=%s") % rpr(self.national_prefix_optional_when_formatting)
134+
result += u(", national_prefix_optional_when_formatting=%s") % str(self.national_prefix_optional_when_formatting)
135135
if self.domestic_carrier_code_formatting_rule is not None:
136136
result += u(", domestic_carrier_code_formatting_rule=%s") % rpr(self.domestic_carrier_code_formatting_rule)
137137
result += u(")")

python/phonenumbers/phonenumber.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@ def __ne__(self, other):
185185
def __repr__(self):
186186
return (("PhoneNumber(country_code=%s, national_number=%s, extension=%s, " +
187187
"italian_leading_zero=%s, country_code_source=%s, preferred_domestic_carrier_code=%s)") %
188-
(rpr(self.country_code),
189-
rpr(self.national_number),
188+
(self.country_code,
189+
self.national_number,
190190
rpr(self.extension),
191-
rpr(self.italian_leading_zero),
192-
rpr(self.country_code_source),
191+
self.italian_leading_zero,
192+
self.country_code_source,
193193
rpr(self.preferred_domestic_carrier_code)))
194194

195195
def __unicode__(self):

python/phonenumbers/util.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@
2424
import builtins
2525
print3 = builtins.__dict__['print']
2626

27+
unicod = str
2728
u = str
2829
uchr = chr
2930
to_long = int
30-
# TODO create a Py3k repr-equivalent that produces something
31-
# parsable in Python 2 (with the assistance of this module)
32-
rpr = repr
3331

3432
def prnt(*args, **kwargs):
3533
sep = kwargs.get('sep', ' ')
@@ -41,6 +39,8 @@ class UnicodeMixin(object):
4139
__str__ = lambda x: x.__unicode__()
4240

4341
else: # pragma no cover
42+
unicod = unicode
43+
4444
import unicodedata
4545
import re
4646
# \N{name} = character named name in the Unicode database
@@ -59,18 +59,6 @@ def u(s):
5959
uchr = unichr
6060
to_long = long
6161

62-
_U_SQ_RE = re.compile("^u('[^']*')")
63-
_U_DQ_RE = re.compile('^u("[^"]*")')
64-
_X_LATIN1_RE = re.compile(r"(?P<x>\\x)(?P<hexval>[0-9a-fA-Z]{2})")
65-
def rpr(obj):
66-
s = repr(obj)
67-
# Assume any \xYY sequences are taking advantage of Python 2's default
68-
# Latin-1 string encoding
69-
s = re.sub(_X_LATIN1_RE, '\\u00\g<hexval>', s)
70-
s = re.sub(_U_SQ_RE, r'u(\1)', s)
71-
s = re.sub(_U_DQ_RE, r'u(\1)', s)
72-
return s
73-
7462
def prnt(*args, **kwargs):
7563
sep = kwargs.get('sep', ' ')
7664
end = kwargs.get('end', '\n')
@@ -83,6 +71,39 @@ class UnicodeMixin(object): # pragma no cover
8371
__str__ = lambda x: unicode(x).encode('utf-8')
8472

8573

74+
def rpr(s):
75+
"""Create a representation of a Unicode string that can be used in both
76+
Python 2 and Python 3k, allowing for use of the u() function"""
77+
if s is None:
78+
return 'None'
79+
seen_unicode = False
80+
results = []
81+
for cc in s:
82+
ccn = ord(cc)
83+
if ccn >= 32 and ccn < 127:
84+
if cc == "'": # escape single quote
85+
results.append('\\')
86+
results.append(cc)
87+
elif cc == "\\": # escape backslash
88+
results.append('\\')
89+
results.append(cc)
90+
else:
91+
results.append(cc)
92+
else:
93+
seen_unicode = True
94+
if ccn <= 0xFFFF:
95+
results.append('\\u')
96+
results.append("%04x" % ccn)
97+
else:
98+
results.append('\\U')
99+
results.append("%08x" % ccn)
100+
result = "'" + "".join(results) + "'"
101+
if seen_unicode:
102+
return "u(" + result + ")"
103+
else:
104+
return result
105+
106+
86107
if __name__ == '__main__': # pragma no cover
87108
import doctest
88109
doctest.testmod()

python/tests/phonenumberutiltest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,8 @@ def testFormatWithPreferredCarrierCode(self):
574574
self.assertEqual('Country Code: 54 National Number: 91234125678 '
575575
'Leading Zero: False Preferred Domestic Carrier Code: 19',
576576
str(arNumber))
577-
self.assertEqual("PhoneNumber(country_code=54, national_number=91234125678%s, extension=None, "
578-
"italian_leading_zero=False, country_code_source=None, preferred_domestic_carrier_code='19')" % _LS,
577+
self.assertEqual("PhoneNumber(country_code=54, national_number=91234125678, extension=None, "
578+
"italian_leading_zero=False, country_code_source=None, preferred_domestic_carrier_code='19')",
579579
repr(arNumber))
580580
# When the preferred_domestic_carrier_code is present (even when it
581581
# contains an empty string), use it instead of the default carrier
@@ -2003,7 +2003,7 @@ def testMetadataAsString(self):
20032003
metadata = PhoneMetadata.region_metadata["AU"]
20042004
self.assertEqual('\\' + 'd',
20052005
metadata.number_format[0].pattern[1:3])
2006-
self.assertEqual(r"""NumberFormat(pattern='(\\d{4})(\\d{3})(\\d{3})', format=u('\\1 \\2 \\3'), leading_digits_pattern=['1'], national_prefix_formatting_rule=u('\\1'))""",
2006+
self.assertEqual(r"""NumberFormat(pattern='(\\d{4})(\\d{3})(\\d{3})', format='\\1 \\2 \\3', leading_digits_pattern=['1'], national_prefix_formatting_rule='\\1')""",
20072007
str(metadata.number_format[0]))
20082008
self.assertEqual(repr(metadata.number_format[0]),
20092009
str(metadata.number_format[0]))
@@ -2072,8 +2072,8 @@ def testMetadataAsString(self):
20722072
preferred_international_prefix='0011',
20732073
national_prefix='0',
20742074
national_prefix_for_parsing='0',
2075-
number_format=[NumberFormat(pattern='(\\d{4})(\\d{3})(\\d{3})', format=u('\\1 \\2 \\3'), leading_digits_pattern=['1'], national_prefix_formatting_rule=u('\\1')),
2076-
NumberFormat(pattern='(\\d{1})(\\d{4})(\\d{4})', format=u('\\1 \\2 \\3'), leading_digits_pattern=['[2-478]'], national_prefix_formatting_rule=u('0\\1'))])""",
2075+
number_format=[NumberFormat(pattern='(\\d{4})(\\d{3})(\\d{3})', format='\\1 \\2 \\3', leading_digits_pattern=['1'], national_prefix_formatting_rule='\\1'),
2076+
NumberFormat(pattern='(\\d{1})(\\d{4})(\\d{4})', format='\\1 \\2 \\3', leading_digits_pattern=['[2-478]'], national_prefix_formatting_rule='0\\1')])""",
20772077
str(metadata))
20782078

20792079
def testMetadataEval(self):

0 commit comments

Comments
 (0)