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) |