Gentoo Archives: gentoo-portage-dev

From: "Michał Górny" <mgorny@g.o>
To: "Anthony G. Basile" <basile@××××××××××××××.edu>
Cc: gentoo-portage-dev@l.g.o
Subject: Re: [gentoo-portage-dev] [PATCH 3/3] pym/portage/util/locale.py: add a C module to help check locale
Date: Sun, 29 May 2016 07:13:33
Message-Id: 20160529091320.6244ea2c.mgorny@gentoo.org
In Reply to: Re: [gentoo-portage-dev] [PATCH 3/3] pym/portage/util/locale.py: add a C module to help check locale by "Anthony G. Basile"
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/>