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 help check locale
Date: Sun, 22 May 2016 17:05:16
Message-Id: 1463936680-16072-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 a sane system locale is to use python's
4 ctypes.util.find_library() to construct a full library path to the
5 system libc.so and pass that path to ctypes.CDLL() so we can call
6 toupper() and tolower() directly. However, this gets bogged down in
7 implementation details and fails with musl.
8
9 We work around this design flaw in ctypes with a small python module
10 written in C which provides thin wrappers to toupper() and tolower(),
11 and only fall back on the current ctypes-based check when this module
12 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
19 Signed-off-by: Anthony G. Basile <blueness@g.o>
20 ---
21 pym/portage/util/locale.py | 32 ++++++++++-----
22 setup.py | 6 ++-
23 src/portage_c_convert_case.c | 94 ++++++++++++++++++++++++++++++++++++++++++++
24 3 files changed, 121 insertions(+), 11 deletions(-)
25 create mode 100644 src/portage_c_convert_case.c
26
27 diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py
28 index 2a15ea1..85ddd2b 100644
29 --- a/pym/portage/util/locale.py
30 +++ b/pym/portage/util/locale.py
31 @@ -11,6 +11,7 @@ from __future__ import absolute_import, unicode_literals
32 import locale
33 import logging
34 import os
35 +import sys
36 import textwrap
37 import traceback
38
39 @@ -34,18 +35,26 @@ def _check_locale(silent):
40 """
41 The inner locale check function.
42 """
43 -
44 - libc_fn = find_library("c")
45 - if libc_fn is None:
46 - return None
47 - libc = LoadLibrary(libc_fn)
48 - if libc is None:
49 - return None
50 + try:
51 + from portage_c_convert_case import _c_toupper, _c_tolower
52 + libc_tolower = _c_tolower
53 + libc_toupper = _c_toupper
54 + except ImportError:
55 + writemsg_level("!!! Unable to import portage_c_convert_case\n!!!\n",
56 + level=logging.WARNING, noiselevel=-1)
57 + libc_fn = find_library("c")
58 + if libc_fn is None:
59 + return None
60 + libc = LoadLibrary(libc_fn)
61 + if libc is None:
62 + return None
63 + libc_tolower = libc.tolower
64 + libc_toupper = libc.toupper
65
66 lc = list(range(ord('a'), ord('z')+1))
67 uc = list(range(ord('A'), ord('Z')+1))
68 - rlc = [libc.tolower(c) for c in uc]
69 - ruc = [libc.toupper(c) for c in lc]
70 + rlc = [libc_tolower(c) for c in uc]
71 + ruc = [libc_toupper(c) for c in lc]
72
73 if lc != rlc or uc != ruc:
74 if silent:
75 @@ -62,7 +71,10 @@ def _check_locale(silent):
76 "as LC_CTYPE in make.conf.")
77 msg = [l for l in textwrap.wrap(msg, 70)]
78 msg.append("")
79 - chars = lambda l: ''.join(chr(x) for x in l)
80 + if sys.version_info.major >= 3:
81 + chars = lambda l: ''.join(chr(x) for x in l)
82 + else:
83 + chars = lambda l: ''.join(chr(x).decode('utf-8', 'replace') for x in l)
84 if uc != ruc:
85 msg.extend([
86 " %s -> %s" % (chars(lc), chars(ruc)),
87 diff --git a/setup.py b/setup.py
88 index 25429bc..8b6b408 100755
89 --- a/setup.py
90 +++ b/setup.py
91 @@ -47,7 +47,11 @@ x_scripts = {
92 # Dictionary custom modules written in C/C++ here. The structure is
93 # key = module name
94 # value = list of C/C++ source code, path relative to top source directory
95 -x_c_helpers = {}
96 +x_c_helpers = {
97 + 'portage_c_convert_case' : [
98 + 'src/portage_c_convert_case.c',
99 + ],
100 +}
101
102 class x_build(build):
103 """ Build command with extra build_man call. """
104 diff --git a/src/portage_c_convert_case.c b/src/portage_c_convert_case.c
105 new file mode 100644
106 index 0000000..f60b0c2
107 --- /dev/null
108 +++ b/src/portage_c_convert_case.c
109 @@ -0,0 +1,94 @@
110 +/* Copyright 2005-2016 Gentoo Foundation
111 + * Distributed under the terms of the GNU General Public License v2
112 + */
113 +
114 +#include <Python.h>
115 +#include <ctype.h>
116 +
117 +static PyObject * portage_c_tolower(PyObject *, PyObject *);
118 +static PyObject * portage_c_toupper(PyObject *, PyObject *);
119 +
120 +static PyMethodDef ConvertCaseMethods[] = {
121 + {"_c_tolower", portage_c_tolower, METH_VARARGS, "Convert to lower case using system locale."},
122 + {"_c_toupper", portage_c_toupper, METH_VARARGS, "Convert to upper case using system locale."},
123 + {NULL, NULL, 0, NULL}
124 +};
125 +
126 +#if PY_MAJOR_VERSION >= 3
127 +static struct PyModuleDef moduledef = {
128 + PyModuleDef_HEAD_INIT,
129 + "portage_c_convert_case", /* m_name */
130 + "Module for converting case using the system locale", /* m_doc */
131 + -1, /* m_size */
132 + ConvertCaseMethods, /* m_methods */
133 + NULL, /* m_reload */
134 + NULL, /* m_traverse */
135 + NULL, /* m_clear */
136 + NULL, /* m_free */
137 +};
138 +#endif
139 +
140 +static PyObject *ConvertCaseError;
141 +
142 +PyMODINIT_FUNC
143 +#if PY_MAJOR_VERSION >= 3
144 +PyInit_portage_c_convert_case(void)
145 +#else
146 +initportage_c_convert_case(void)
147 +#endif
148 +{
149 + PyObject *m;
150 +
151 +#if PY_MAJOR_VERSION >= 3
152 + m = PyModule_Create(&moduledef);
153 +#else
154 + m = Py_InitModule("portage_c_convert_case", ConvertCaseMethods);
155 +#endif
156 +
157 + if (m == NULL)
158 +#if PY_MAJOR_VERSION >= 3
159 + return NULL;
160 +#else
161 + return;
162 +#endif
163 +
164 + ConvertCaseError = PyErr_NewException("portage_c_convert_case.ConvertCaseError", NULL, NULL);
165 + Py_INCREF(ConvertCaseError);
166 + PyModule_AddObject(m, "ConvertCaseError", ConvertCaseError);
167 +
168 +#if PY_MAJOR_VERSION >= 3
169 + return m;
170 +#else
171 + return;
172 +#endif
173 +}
174 +
175 +
176 +static PyObject *
177 +portage_c_tolower(PyObject *self, PyObject *args)
178 +{
179 + int c;
180 +
181 + if (!PyArg_ParseTuple(args, "i", &c))
182 + {
183 + PyErr_SetString(ConvertCaseError, "_c_tolower: PyArg_ParseTuple failed");
184 + return NULL;
185 + }
186 +
187 + return Py_BuildValue("i", tolower(c));
188 +}
189 +
190 +
191 +static PyObject *
192 +portage_c_toupper(PyObject *self, PyObject *args)
193 +{
194 + int c;
195 +
196 + if (!PyArg_ParseTuple(args, "i", &c))
197 + {
198 + PyErr_SetString(ConvertCaseError, "_c_toupper: PyArg_ParseTuple failed");
199 + return NULL;
200 + }
201 +
202 + return Py_BuildValue("i", toupper(c));
203 +}
204 --
205 2.7.3

Replies