Gentoo Archives: gentoo-portage-dev

From: "Anthony G. Basile" <basile@××××××××××××××.edu>
To: gentoo-portage-dev@l.g.o
Cc: "Anthony G. Basile" <blueness@g.o>
Subject: [gentoo-portage-dev] [PATCH 2/2] pym/portage/util/locale.py: add a C module to check locale
Date: Thu, 19 May 2016 12:44:10
Message-Id: 1463661818-6940-2-git-send-email-basile@opensource.dyc.edu
In Reply to: [gentoo-portage-dev] [PATCH 1/2] setup.py: add stub for building custom modules in C/C++ by "Anthony G. Basile"
1 From: "Anthony G. Basile" <blueness@g.o>
2
3 The current method to check for the system locale is to use python's
4 ctypes.util.find_library() to construct a full library path to the
5 system libc.so which is then passed to ctypes.CDLL(). However,
6 this gets bogged down in implementation dependant details and
7 fails with musl.
8
9 We work around this design flaw in ctypes with a small python module
10 written in C called 'portage_c_check_locale', and only fall back on
11 the current ctypes-based check when this module is not available.
12
13 This has been tested on glibc, uClibc and musl systems.
14
15 X-Gentoo-bug: 571444
16 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=571444
17 Signed-off-by: Anthony G. Basile <blueness@g.o>
18 ---
19 pym/portage/util/locale.py | 40 +++++++++----
20 setup.py | 6 +-
21 src/check_locale.c | 144 +++++++++++++++++++++++++++++++++++++++++++++
22 3 files changed, 178 insertions(+), 12 deletions(-)
23 create mode 100644 src/check_locale.c
24
25 diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py
26 index 2a15ea1..fc5d052 100644
27 --- a/pym/portage/util/locale.py
28 +++ b/pym/portage/util/locale.py
29 @@ -30,17 +30,17 @@ locale_categories = (
30 _check_locale_cache = {}
31
32
33 -def _check_locale(silent):
34 +def _ctypes_check_locale():
35 """
36 - The inner locale check function.
37 + Check for locale using ctypes.
38 """
39
40 libc_fn = find_library("c")
41 if libc_fn is None:
42 - return None
43 + return (None, "")
44 libc = LoadLibrary(libc_fn)
45 if libc is None:
46 - return None
47 + return (None, "")
48
49 lc = list(range(ord('a'), ord('z')+1))
50 uc = list(range(ord('A'), ord('Z')+1))
51 @@ -48,9 +48,6 @@ def _check_locale(silent):
52 ruc = [libc.toupper(c) for c in lc]
53
54 if lc != rlc or uc != ruc:
55 - if silent:
56 - return False
57 -
58 msg = ("WARNING: The LC_CTYPE variable is set to a locale " +
59 "that specifies transformation between lowercase " +
60 "and uppercase ASCII characters that is different than " +
61 @@ -71,11 +68,32 @@ def _check_locale(silent):
62 msg.extend([
63 " %s -> %s" % (chars(uc), chars(rlc)),
64 " %28s: %s" % ('expected', chars(lc))])
65 - writemsg_level("".join(["!!! %s\n" % l for l in msg]),
66 - level=logging.ERROR, noiselevel=-1)
67 - return False
68 + msg = "".join(["!!! %s\n" % l for l in msg]),
69 + return (False, msg)
70 +
71 + return (True, "")
72 +
73 +
74 +def _check_locale(silent):
75 + """
76 + The inner locale check function.
77 + """
78 +
79 + try:
80 + from portage_c_check_locale import _c_check_locale
81 + (ret, msg) = _c_check_locale()
82 + except ImportError:
83 + writemsg_level("!!! Unable to import portage_c_check_locale\n",
84 + level=logging.WARNING, noiselevel=-1)
85 + (ret, msg) = _ctypes_check_locale()
86 +
87 + if ret:
88 + return True
89 +
90 + if not silent:
91 + writemsg_level(msg, level=logging.ERROR, noiselevel=-1)
92
93 - return True
94 + return False
95
96
97 def check_locale(silent=False, env=None):
98 diff --git a/setup.py b/setup.py
99 index 25429bc..e44ac41 100755
100 --- a/setup.py
101 +++ b/setup.py
102 @@ -47,7 +47,11 @@ x_scripts = {
103 # Dictionary custom modules written in C/C++ here. The structure is
104 # key = module name
105 # value = list of C/C++ source code, path relative to top source directory
106 -x_c_helpers = {}
107 +x_c_helpers = {
108 + 'portage_c_check_locale' : [
109 + 'src/check_locale.c',
110 + ],
111 +}
112
113 class x_build(build):
114 """ Build command with extra build_man call. """
115 diff --git a/src/check_locale.c b/src/check_locale.c
116 new file mode 100644
117 index 0000000..9762ef2
118 --- /dev/null
119 +++ b/src/check_locale.c
120 @@ -0,0 +1,144 @@
121 +/* Copyright 2005-2015 Gentoo Foundation
122 + * Distributed under the terms of the GNU General Public License v2
123 + */
124 +
125 +#include <Python.h>
126 +#include <ctype.h>
127 +#include <string.h>
128 +
129 +static PyObject * portage_c_check_locale(PyObject *, PyObject *);
130 +
131 +static PyMethodDef CheckLocaleMethods[] = {
132 + {"_c_check_locale", portage_c_check_locale, METH_NOARGS, "Check the system locale."},
133 + {NULL, NULL, 0, NULL}
134 +};
135 +
136 +#if PY_MAJOR_VERSION >= 3
137 +static struct PyModuleDef moduledef = {
138 + PyModuleDef_HEAD_INIT,
139 + "portage_c_check_locale", /* m_name */
140 + "Module for checking the system locale for portage", /* m_doc */
141 + -1, /* m_size */
142 + CheckLocaleMethods, /* m_methods */
143 + NULL, /* m_reload */
144 + NULL, /* m_traverse */
145 + NULL, /* m_clear */
146 + NULL, /* m_free */
147 +};
148 +#endif
149 +
150 +static PyObject *LocaleError;
151 +
152 +PyMODINIT_FUNC
153 +#if PY_MAJOR_VERSION >= 3
154 +PyInit_portage_c_check_locale(void)
155 +#else
156 +initportage_c_check_locale(void)
157 +#endif
158 +{
159 + PyObject *m;
160 +
161 +#if PY_MAJOR_VERSION >= 3
162 + m = PyModule_Create(&moduledef);
163 +#else
164 + m = Py_InitModule("portage_c_check_locale", CheckLocaleMethods);
165 +#endif
166 +
167 + if (m == NULL)
168 +#if PY_MAJOR_VERSION >= 3
169 + return NULL;
170 +#else
171 + return;
172 +#endif
173 +
174 + LocaleError = PyErr_NewException("locale.LocaleError", NULL, NULL);
175 + Py_INCREF(LocaleError);
176 + PyModule_AddObject(m, "LocaleError", LocaleError);
177 +
178 +#if PY_MAJOR_VERSION >= 3
179 + return m;
180 +#else
181 + return;
182 +#endif
183 +}
184 +
185 +
186 +static void
187 +error_msg(char msg[], int c[], int rc[], int ac[])
188 +{
189 + int i, p;
190 +
191 + strcat(msg, " ");
192 + p = strlen(msg);
193 + for (i = 'a'; i <= 'z'; i++)
194 + msg[p++] = c[i-'a'];
195 +
196 + strcat(msg, " -> ");
197 + p = strlen(msg);
198 + for (i = 'a'; i <= 'z'; i++)
199 + msg[p++] = rc[i-'a'];
200 + strcat(msg, "\n");
201 +
202 + strcat(msg, " expected: ");
203 + p = strlen(msg);
204 + for (i = 'a'; i <= 'z'; i++)
205 + msg[p++] = ac[i-'a'];
206 + strcat(msg, "\n\n");
207 +}
208 +
209 +
210 +static PyObject *
211 +portage_c_check_locale(PyObject *self, PyObject *args)
212 +{
213 + int i, upper = 1, lower = 1;
214 + int lc[26], uc[26], rlc[26], ruc[26];
215 + char msg[1024];
216 +
217 + memset(msg, 0, 1024);
218 +
219 + for (i = 'a'; i <= 'z'; i++) {
220 + lc[i-'a'] = i;
221 + ruc[i-'a'] = toupper(i);
222 + }
223 +
224 + for (i = 'A'; i <= 'Z'; i++) {
225 + uc[i-'A'] = i;
226 + rlc[i-'A'] = tolower(i);
227 + }
228 +
229 + for (i = 'a'; i <= 'z'; i++)
230 + if(lc[i-'a'] != rlc[i-'a']) {
231 + lower = 0;
232 + break;
233 + }
234 +
235 + for (i = 'A'; i <= 'Z'; i++)
236 + if(uc[i-'A'] != ruc[i-'A']) {
237 + upper = 0;
238 + break;
239 + }
240 +
241 + if (lower == 0 || upper == 0) {
242 + strcpy(msg,
243 + "!!! WARNING: The LC_CTYPE variable is set to a locale that specifies\n"
244 + "!!! transformation between lowercase and uppercase ASCII characters that\n"
245 + "!!! is different than the one specified by POSIX locale. This can break\n"
246 + "!!! ebuilds and cause issues in programs that rely on the common character\n"
247 + "!!! conversion scheme. Please consider enabling another locale (such as\n"
248 + "!!! en_US.UTF-8) in /etc/locale.gen and setting it as LC_CTYPE in\n"
249 + "!!! make.conf.\n\n"
250 + );
251 +
252 + if (lower == 0)
253 + error_msg(msg, lc, ruc, uc);
254 +
255 + if (upper == 0)
256 + error_msg(msg, uc, rlc, lc);
257 + }
258 +
259 +#if PY_MAJOR_VERSION >= 3
260 + return Py_BuildValue("iy", lower && upper, msg);
261 +#else
262 + return Py_BuildValue("is", lower && upper, msg);
263 +#endif
264 +}
265 --
266 2.7.3

Replies