Gentoo Archives: gentoo-portage-dev

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

Replies

Subject Author
Re: [gentoo-portage-dev] [PATCH 2/2] pym/portage/util/locale.py: add a C module to check locale "Anthony G. Basile" <basile@××××××××××××××.edu>