Skip to content

Commit 9524f75

Browse files
committed
[GR-23690] Python locale settings per Python context.
PullRequest: graalpython/977
2 parents 2e15b24 + 49f4433 commit 9524f75

File tree

10 files changed

+561
-208
lines changed

10 files changed

+561
-208
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
1313
* Expand support for the following modules: llvmlite, pydantic-core, catboost, ray, tensorflow, tensorflow-io, readme-renderer, safetensors, keras, pybind11, protbuf, grpcio, PyO3, cryptography, bcrypt, cramjam, libcst, orjson, rpds_py.
1414
* Support installing some packages with native extensions on Windows. Simple packages like `ujson` or `kiwisolver` will now work when installed from a venv inside a Visual Studio command prompt.
1515
* Raise `KeyboardInterrupt` exception when the process is interrupted. Enabled only when using the launcher.
16+
* Add option `python.InitialLocale` to change the default locale. If not set, then Java Locale#getDefault is used.
1617

1718
## Version 23.1.0
1819
* Oracle GraalPy distributions (previously known as GraalPy Enterprise) are now available under the [GFTC license](https://www.oracle.com/downloads/licenses/graal-free-license.html). The community builds published on Github have been renamed to `graalpy-community-<version>-<os>-<arch>.tar.gz`.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.test.integration.module;
42+
43+
import static com.oracle.graal.python.test.integration.PythonTests.assertPrints;
44+
import static org.junit.Assert.assertEquals;
45+
46+
import java.nio.charset.Charset;
47+
import java.util.Locale;
48+
49+
import org.graalvm.polyglot.Context;
50+
import org.graalvm.polyglot.Value;
51+
import org.junit.Test;
52+
53+
public class LocaleTest {
54+
@Test
55+
public void getlocaleWithJvmLocale() {
56+
String expectedEncoding = Charset.defaultCharset().displayName();
57+
String expectedOutput = String.format("('it_IT', '%s')\n", expectedEncoding);
58+
Locale currentDefault = Locale.getDefault();
59+
try {
60+
Locale.setDefault(Locale.ITALY);
61+
assertPrints(expectedOutput, "import locale; print(locale.getlocale())");
62+
} finally {
63+
Locale.setDefault(currentDefault);
64+
}
65+
}
66+
67+
@Test
68+
public void localeconvWithJvmLocale() {
69+
Locale currentDefault = Locale.getDefault();
70+
try {
71+
Locale.setDefault(Locale.ITALY);
72+
assertPrints("EUR\n", "import locale; print(locale.localeconv()['int_curr_symbol'])");
73+
} finally {
74+
Locale.setDefault(currentDefault);
75+
}
76+
}
77+
78+
@Test
79+
public void getlocaleWithOption() {
80+
String expectedEncoding = Charset.defaultCharset().displayName();
81+
try (Context context = Context.newBuilder("python").option("python.InitialLocale", "en_GB").build()) {
82+
Value tuple = context.eval("python", "import locale; locale.getlocale()");
83+
assertEquals("en_GB", tuple.getArrayElement(0).asString());
84+
assertEquals(expectedEncoding, tuple.getArrayElement(1).asString());
85+
86+
Value currency = context.eval("python", "import locale; locale.localeconv()['int_curr_symbol']");
87+
assertEquals("GBP", currency.asString());
88+
}
89+
}
90+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.test.util;
42+
43+
import java.util.Locale;
44+
45+
import org.junit.Assert;
46+
import org.junit.Test;
47+
48+
import com.oracle.graal.python.runtime.locale.LocaleUtils;
49+
50+
public class LocaleUtilsTest {
51+
@Test
52+
public void testFromPosix() {
53+
assertEquals("en", null, "en");
54+
assertEquals("en", "GB", "en_GB");
55+
assertEquals("en", "GB", "en_GB.UTF-8");
56+
assertEquals("en", null, "en.UTF-8");
57+
assertEquals("en", null, "en@posix");
58+
assertEquals("en", "GB", "en_GB@posix");
59+
assertEquals("en", "GB", "en_GB.UTF-8@posix");
60+
61+
Assert.assertEquals(Locale.ROOT, LocaleUtils.fromPosix("C", null));
62+
Assert.assertEquals(Locale.GERMANY, LocaleUtils.fromPosix("", Locale.GERMANY));
63+
}
64+
65+
@Test
66+
public void testFromPosixErrors() {
67+
assertDefault("q");
68+
assertDefault("@posix");
69+
assertDefault("UTF-8");
70+
assertDefault(".UTF-8");
71+
}
72+
73+
private static void assertDefault(String wrongId) {
74+
Assert.assertNull(LocaleUtils.fromPosix(wrongId, Locale.ROOT));
75+
}
76+
77+
private static void assertEquals(String expectedLanguage, String expectedCountry, String actualID) {
78+
Locale l = LocaleUtils.fromPosix(actualID, null);
79+
Assert.assertNotNull(l);
80+
Assert.assertEquals(expectedLanguage, l.getLanguage());
81+
if (expectedCountry != null) {
82+
Assert.assertEquals(expectedCountry, l.getCountry());
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)