1 |
On Fri, 27 May 2016 10:26:44 -0400 |
2 |
"Anthony G. Basile" <basile@××××××××××××××.edu> wrote: |
3 |
|
4 |
> From: "Anthony G. Basile" <blueness@g.o> |
5 |
> |
6 |
> The current method to check for a sane system locale is to use python's |
7 |
> ctypes.util.find_library() to construct a full library path to the |
8 |
> system libc.so and pass that path to ctypes.CDLL() so we can call |
9 |
> toupper() and tolower() directly. However, this gets bogged down in |
10 |
> implementation details and fails with musl. |
11 |
> |
12 |
> We work around this design flaw in ctypes with a small python module |
13 |
> written in C which provides thin wrappers to toupper() and tolower(), |
14 |
> and only fall back on the current ctypes-based check when this module |
15 |
> is not available. |
16 |
> |
17 |
> This has been tested on glibc, uClibc and musl systems. |
18 |
> |
19 |
> X-Gentoo-bug: 571444 |
20 |
> X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=571444 |
21 |
> |
22 |
> Signed-off-by: Anthony G. Basile <blueness@g.o> |
23 |
> --- |
24 |
> pym/portage/util/locale.py | 16 ++++++----- |
25 |
> setup.py | 6 +++- |
26 |
> src/portage_util_libc.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ |
27 |
> 3 files changed, 84 insertions(+), 8 deletions(-) |
28 |
> create mode 100644 src/portage_util_libc.c |
29 |
> |
30 |
> diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py |
31 |
> index 093eb86..5b09945 100644 |
32 |
> --- a/pym/portage/util/locale.py |
33 |
> +++ b/pym/portage/util/locale.py |
34 |
> @@ -34,13 +34,15 @@ def _check_locale(silent): |
35 |
> """ |
36 |
> The inner locale check function. |
37 |
> """ |
38 |
> - |
39 |
> - libc_fn = find_library("c") |
40 |
> - if libc_fn is None: |
41 |
> - return None |
42 |
> - libc = LoadLibrary(libc_fn) |
43 |
> - if libc is None: |
44 |
> - return None |
45 |
> + try: |
46 |
> + from portage.util import libc |
47 |
> + except ImportError: |
48 |
> + libc_fn = find_library("c") |
49 |
> + if libc_fn is None: |
50 |
> + return None |
51 |
> + libc = LoadLibrary(libc_fn) |
52 |
> + if libc is None: |
53 |
> + return None |
54 |
> |
55 |
> lc = list(range(ord('a'), ord('z')+1)) |
56 |
> uc = list(range(ord('A'), ord('Z')+1)) |
57 |
> diff --git a/setup.py b/setup.py |
58 |
> index 25429bc..5ca8156 100755 |
59 |
> --- a/setup.py |
60 |
> +++ b/setup.py |
61 |
> @@ -47,7 +47,11 @@ x_scripts = { |
62 |
> # Dictionary custom modules written in C/C++ here. The structure is |
63 |
> # key = module name |
64 |
> # value = list of C/C++ source code, path relative to top source directory |
65 |
> -x_c_helpers = {} |
66 |
> +x_c_helpers = { |
67 |
> + 'portage.util.libc' : [ |
68 |
> + 'src/portage_util_libc.c', |
69 |
> + ], |
70 |
> +} |
71 |
> |
72 |
> class x_build(build): |
73 |
> """ Build command with extra build_man call. """ |
74 |
> diff --git a/src/portage_util_libc.c b/src/portage_util_libc.c |
75 |
> new file mode 100644 |
76 |
> index 0000000..00b09c2 |
77 |
> --- /dev/null |
78 |
> +++ b/src/portage_util_libc.c |
79 |
> @@ -0,0 +1,70 @@ |
80 |
> +/* Copyright 2005-2016 Gentoo Foundation |
81 |
> + * Distributed under the terms of the GNU General Public License v2 |
82 |
> + */ |
83 |
> + |
84 |
> +#include <Python.h> |
85 |
> +#include <stdlib.h> |
86 |
> +#include <ctype.h> |
87 |
> + |
88 |
> +static PyObject * _libc_tolower(PyObject *, PyObject *); |
89 |
> +static PyObject * _libc_toupper(PyObject *, PyObject *); |
90 |
> + |
91 |
> +static PyMethodDef LibcMethods[] = { |
92 |
> + {"tolower", _libc_tolower, METH_VARARGS, "Convert to lower case using system locale."}, |
93 |
> + {"toupper", _libc_toupper, METH_VARARGS, "Convert to upper case using system locale."}, |
94 |
> + {NULL, NULL, 0, NULL} |
95 |
> +}; |
96 |
> + |
97 |
> +#if PY_MAJOR_VERSION >= 3 |
98 |
> +static struct PyModuleDef moduledef = { |
99 |
> + PyModuleDef_HEAD_INIT, |
100 |
> + "libc", /* m_name */ |
101 |
> + "Module for converting case using the system locale", /* m_doc */ |
102 |
> + -1, /* m_size */ |
103 |
> + LibcMethods, /* m_methods */ |
104 |
> + NULL, /* m_reload */ |
105 |
> + NULL, /* m_traverse */ |
106 |
> + NULL, /* m_clear */ |
107 |
> + NULL, /* m_free */ |
108 |
> +}; |
109 |
> +#endif |
110 |
> + |
111 |
> +PyMODINIT_FUNC |
112 |
|
113 |
Now you could call it nitpicking but since it decorates the function, |
114 |
it would be better to have it inside the #ifdef. Otherwise, people |
115 |
would think it's separate from that. |
116 |
|
117 |
> + |
118 |
> +#if PY_MAJOR_VERSION >= 3 |
119 |
> +PyInit_libc(void) |
120 |
> +{ |
121 |
> + PyObject *m; |
122 |
> + m = PyModule_Create(&moduledef); |
123 |
> + return m; |
124 |
> +} |
125 |
> +#else |
126 |
> +initlibc(void) |
127 |
> +{ |
128 |
> + Py_InitModule("libc", LibcMethods); |
129 |
> +} |
130 |
> +#endif |
131 |
> + |
132 |
> + |
133 |
> +static PyObject * |
134 |
> +_libc_tolower(PyObject *self, PyObject *args) |
135 |
> +{ |
136 |
> + int c; |
137 |
> + |
138 |
> + if (!PyArg_ParseTuple(args, "i", &c)) |
139 |
> + return NULL; |
140 |
> + |
141 |
> + return Py_BuildValue("i", tolower(c)); |
142 |
> +} |
143 |
> + |
144 |
> + |
145 |
> +static PyObject * |
146 |
> +_libc_toupper(PyObject *self, PyObject *args) |
147 |
> +{ |
148 |
> + int c; |
149 |
> + |
150 |
> + if (!PyArg_ParseTuple(args, "i", &c)) |
151 |
> + return NULL; |
152 |
> + |
153 |
> + return Py_BuildValue("i", toupper(c)); |
154 |
> +} |
155 |
|
156 |
Aside from that, it look nice and shiny now. Thanks a lot! |
157 |
|
158 |
-- |
159 |
Best regards, |
160 |
Michał Górny |
161 |
<http://dev.gentoo.org/~mgorny/> |