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