Skip to content

Commit 629182b

Browse files
committed
Get rid of the PyRuntime class
PyRuntime was an attempt to run the tests against multiple pythons. However, it was clumsy, and contained hardcoded values (for python 2.7) This patch removes it, instead simply building with sys.executable Doing so means that we're now building and running the selftests against the version of python that the plugin was built against, rather than hardcoding /usr/bin/python2.7 In particular, when building the plugin for Python 3, it means that the selftests are now testing compiling against the Python 3 headers. Doing this located some real bugs for this case, which this patch also fixes.
1 parent d6630ed commit 629182b

File tree

8 files changed

+105
-111
lines changed

8 files changed

+105
-111
lines changed

cpybuilder.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -516,51 +516,3 @@ def from_text(cls, txt):
516516
micro=int(m.group(3)),
517517
releaselevel=m.group(4),
518518
serial=int(m.group(5)))
519-
520-
class PyRuntime:
521-
def __init__(self, executable, config):
522-
self.executable = executable
523-
self.config = config
524-
525-
pydebug = self.run_command('import sys; print(hasattr(sys, "getobjects"))').strip()
526-
assert pydebug in ('False', 'True')
527-
self.pydebug = (pydebug == 'True')
528-
529-
versiontext = self.run_command('import sys; print(sys.version_info)').strip()
530-
self.versioninfo = PyVersionInfo.from_text(versiontext)
531-
532-
def is_py3k(self):
533-
return self.versioninfo.major == 3
534-
535-
def get_build_flags(self):
536-
return self.get_config_value(['--cflags', '--ldflags'])
537-
538-
def get_config_value(self, flags):
539-
args = [self.config] + flags
540-
p = Popen(args, stdout=PIPE, stderr=PIPE)
541-
out, err = p.communicate()
542-
if six.PY3:
543-
out = out.decode()
544-
err = err.decode()
545-
return ' '.join(out.splitlines()).strip()
546-
547-
def run_command(self, cmd, checkoutput=True):
548-
p = Popen([self.executable, '-c', cmd],
549-
stdout=PIPE, stderr=PIPE)
550-
out, err = p.communicate()
551-
if six.PY3:
552-
out = out.decode()
553-
err = err.decode()
554-
if p.returncode != 0:
555-
raise PyRuntimeError(self, cmd, out, err, p)
556-
return out
557-
558-
#def compile_simple_module(self, sm):
559-
560-
def get_module_filename(self, name):
561-
# FIXME: this is a Fedora-ism:
562-
# FIXME: support for 3.2 onwards also?
563-
if self.pydebug:
564-
return '%s_d.so' % name
565-
else:
566-
return '%s.so' % name

libcpychecker/PyArg_ParseTuple.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,14 @@ def from_string(cls, fmt_string, with_size_t):
327327
else:
328328
result.add_argument('u', [Py_UNICODE().pointer.pointer])
329329
elif c == 'S':
330-
result.add_argument('S', [(get_PyStringObject().pointer.pointer,
331-
get_PyObject().pointer.pointer)])
330+
if is_py3k():
331+
# S (bytes) [PyBytesObject *] (or PyObject *)
332+
result.add_argument('S', [(get_PyBytesObject().pointer.pointer,
333+
get_PyObject().pointer.pointer)])
334+
else:
335+
# S (string) [PyStringObject *] (or PyObject *)
336+
result.add_argument('S', [(get_PyStringObject().pointer.pointer,
337+
get_PyObject().pointer.pointer)])
332338
elif c == 'U':
333339
result.add_argument('U', [(get_PyUnicodeObject().pointer.pointer,
334340
get_PyObject().pointer.pointer)])

libcpychecker/refcounts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def get_transitions_for_function_call(self, state, stmt):
104104
# for this state by assigning it with a special "DeallocatedMemory"
105105
# value
106106
# Clear the value for any fields within the region:
107-
for k, v in region.fields.iteritems():
107+
for k, v in region.fields.items():
108108
if v in new.value_for_region:
109109
del new.value_for_region[v]
110110
# Set the default value for the whole region to be "DeallocatedMemory"
@@ -631,7 +631,7 @@ def dump_region(region, title):
631631
print(' %s' % endstate.exception_rvalue)
632632

633633
if i + 1 < len(traces):
634-
print
634+
sys.stdout.write('\n')
635635

636636
def check_refcounts(fun, dump_traces=False, show_traces=False):
637637
"""

libcpychecker/types.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
import gcc
2222
from gccutils import get_global_typedef
2323

24+
def is_py3k():
25+
"""
26+
Is the Python.h we're compiling against python 3?
27+
"""
28+
if get_global_typedef('PyStringObject'):
29+
return False
30+
else:
31+
return True
32+
2433
def get_Py_ssize_t():
2534
return get_global_typedef('Py_ssize_t')
2635

@@ -54,3 +63,7 @@ def get_PyUnicodeObject():
5463

5564
def get_Py_complex():
5665
return get_global_typedef('Py_complex')
66+
67+
# Python 3:
68+
def get_PyBytesObject():
69+
return get_global_typedef('PyBytesObject')

run-test-suite.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def __init__(self, exppath):
6363
# replace long literals with int literals:
6464
expdata = re.sub('([0-9]+)L', '\g<1>', expdata)
6565
expdata = re.sub('(0x[0-9a-f]+)L', '\g<1>', expdata)
66-
66+
expdata = expdata.replace('struct PyStringObject',
67+
'struct PyBytesObject')
6768
# The expected data is for 64-bit builds of Python
6869
# Fix it up for 32-bit builds as necessary:
6970
if six.MAXSIZE == 0x7fffffff:
@@ -239,7 +240,7 @@ def exclude_test(test):
239240
failed_tests = []
240241
for testdir in testdirs:
241242
try:
242-
print('%s: ' % testdir),
243+
sys.stdout.write('%s: ' % testdir)
243244
run_test(testdir)
244245
print('OK')
245246
num_passes += 1

testcpybuilder.py

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
# along with this program. If not, see
1616
# <http://www.gnu.org/licenses/>.
1717

18+
19+
from distutils import sysconfig as sc
1820
import os
1921
import shutil
2022
from subprocess import Popen, PIPE
@@ -26,13 +28,13 @@
2628

2729
from cpybuilder import *
2830

29-
30-
# FIXME: this will need tweaking:
31-
pyruntimes = [PyRuntime('/usr/bin/python2.7', '/usr/bin/python2.7-config'),
32-
#PyRuntime('/usr/bin/python2.7-debug', '/usr/bin/python2.7-debug-config'),
33-
#PyRuntime('/usr/bin/python3.2mu', '/usr/bin/python3.2mu-config'),
34-
#PyRuntime('/usr/bin/python3.2dmu', '/usr/bin/python3.2dmu-config')
35-
]
31+
def get_module_filename(name):
32+
# FIXME: this is a Fedora-ism:
33+
# FIXME: support for 3.2 onwards also?
34+
if hasattr(sys, "getobjects"):
35+
return '%s_d.so' % name
36+
else:
37+
return '%s.so' % name
3638

3739
class CompilationError(CommandError):
3840
def __init__(self, bm):
@@ -43,33 +45,33 @@ def _describe_activity(self):
4345
return 'compiling: %s' % ' '.join(self.bm.args)
4446

4547
class BuiltModule:
46-
"""A test build of a SimpleModule for a PyRuntime, done in a tempdir"""
47-
def __init__(self, sm, runtime):
48+
"""A test build of a SimpleModule using sys.executable, done in a tempdir"""
49+
def __init__(self, sm):
4850
self.sm = sm
49-
self.runtime = runtime
5051

51-
def write_src(self, extra_cflags = None):
52+
def write_src(self, modname, extra_cflags = None):
5253
self.tmpdir = tempfile.mkdtemp()
5354

54-
self.srcfile = os.path.join(self.tmpdir, 'example.c')
55-
self.modfile = os.path.join(self.tmpdir, self.runtime.get_module_filename('example'))
55+
self.srcfile = os.path.join(self.tmpdir, '%s.c' % modname)
56+
self.modfile = os.path.join(self.tmpdir, get_module_filename(modname))
5657

5758
f = open(self.srcfile, 'w')
5859
f.write(self.sm.cu.as_str())
5960
f.close()
6061

6162

6263
def compile_src(self, extra_cflags = None):
63-
cflags = self.runtime.get_build_flags()
6464
self.args = ['gcc']
6565
self.args += ['-o', self.modfile]
66-
self.args += cflags.split()
66+
self.args += ['-I' + sc.get_python_inc(),
67+
'-I' + sc.get_python_inc(plat_specific=True)]
68+
self.args += sc.get_config_var('CFLAGS').split()
6769
self.args += ['-Wall', '-Werror'] # during testing
6870
self.args += ['-shared'] # not sure why this is necessary
6971
if extra_cflags:
7072
self.args += extra_cflags
7173
self.args += [self.srcfile]
72-
#print self.args
74+
# print(self.args)
7375

7476
env = dict(os.environ)
7577
env['LC_ALL'] = 'C'
@@ -85,24 +87,29 @@ def compile_src(self, extra_cflags = None):
8587
raise CompilationError(self)
8688

8789
assert os.path.exists(self.modfile)
90+
# print(self.modfile)
8891

89-
def build(self, extra_cflags = None):
90-
self.write_src()
92+
def build(self, modname, extra_cflags = None):
93+
self.write_src(modname)
9194
self.compile_src(extra_cflags)
9295

93-
def run_command(self, cmd):
94-
"""Run the command (using the appropriate PyRuntime), adjusting sys.path first"""
95-
out = self.runtime.run_command('import sys; sys.path.append("%s"); %s' % (self.tmpdir, cmd))
96-
return out
97-
9896
def cleanup(self):
9997
shutil.rmtree(self.tmpdir)
10098

10199
class SimpleTest(unittest.TestCase):
102-
#def __init__(self, pyruntime):
103-
# self.pytun
100+
101+
# We'll be manipulating sys.path during the test
102+
# Save a copy, and restore it after each test:
103+
def setUp(self):
104+
self.saved_sys_path = sys.path
105+
sys.path = sys.path[:]
106+
107+
def tearDown(self):
108+
sys.path = self.saved_sys_path
109+
104110
def test_simple_compilation(self):
105111
# Verify building and running a trivial module (against multiple Python runtimes)
112+
MODNAME = 'simple_compilation'
106113
sm = SimpleModule()
107114

108115
sm.cu.add_decl("""
@@ -123,23 +130,24 @@ def test_simple_compilation(self):
123130
METH_VARARGS, 'Return a greeting.')])
124131
sm.cu.add_defn(methods.c_defn())
125132

126-
sm.add_module_init('example', modmethods=methods, moddoc='This is a doc string')
133+
sm.add_module_init(MODNAME, modmethods=methods, moddoc='This is a doc string')
127134
# print sm.cu.as_str()
128135

129-
for runtime in pyruntimes:
130-
# Build the module:
131-
bm = BuiltModule(sm, runtime)
132-
bm.build()
136+
# Build the module:
137+
bm = BuiltModule(sm)
138+
bm.build(MODNAME)
133139

134-
# Verify that it built:
135-
out = bm.run_command('import example; print(example.hello())')
136-
self.assertEqual(out, "Hello world!\n")
140+
# Verify that it built:
141+
sys.path.append(bm.tmpdir)
142+
import simple_compilation
143+
self.assertEqual(simple_compilation.hello(), 'Hello world!')
137144

138-
# Cleanup successful test runs:
139-
bm.cleanup()
145+
# Cleanup successful test runs:
146+
bm.cleanup()
140147

141148
def test_module_with_type(self):
142149
# Verify an extension with a type
150+
MODNAME = 'module_with_type'
143151
sm = SimpleModule()
144152

145153
sm.cu.add_decl("""
@@ -164,20 +172,21 @@ def test_module_with_type(self):
164172
struct_name = 'struct PyExampleType',
165173
tp_repr = 'example_Example_repr')
166174

167-
sm.add_module_init('example', modmethods=None, moddoc='This is a doc string')
175+
sm.add_module_init(MODNAME, modmethods=None, moddoc='This is a doc string')
168176
# print sm.cu.as_str()
169177

170-
for runtime in pyruntimes:
171-
# Build the module:
172-
bm = BuiltModule(sm, runtime)
173-
bm.build()
178+
# Build the module:
179+
bm = BuiltModule(sm)
180+
bm.build(MODNAME)
174181

175-
# Verify that it built:
176-
out = bm.run_command('import example; print(repr(example.ExampleType()))')
177-
self.assertEqual(out, "example.ExampleType('')\n")
182+
# Verify that it built:
183+
sys.path.append(bm.tmpdir)
184+
import module_with_type
185+
self.assertEqual(repr(module_with_type.ExampleType()),
186+
"example.ExampleType('')")
178187

179-
# Cleanup successful test runs:
180-
bm.cleanup()
188+
# Cleanup successful test runs:
189+
bm.cleanup()
181190

182191
def test_version_parsing(self):
183192
vi = PyVersionInfo.from_text("sys.version_info(major=2, minor=7, micro=1, releaselevel='final', serial=0)")

testcpychecker.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,9 @@
2323

2424
import six
2525

26-
from testcpybuilder import BuiltModule, PyRuntime, SimpleModule, CompilationError
26+
from testcpybuilder import BuiltModule, SimpleModule, CompilationError
2727
from cpybuilder import PyMethodTable, PyMethodDef, METH_VARARGS
2828

29-
#FIXME:
30-
pyruntime = PyRuntime('/usr/bin/python2.7', '/usr/bin/python2.7-config')
31-
3229
class ExpectedErrorNotFound(CompilationError):
3330
def __init__(self, expected_err, actual_err, bm):
3431
CompilationError.__init__(self, bm)
@@ -59,7 +56,7 @@ def compile_src(self, bm):
5956
'-fplugin-arg-python-script=cpychecker.py'])
6057

6158
def build_module(self, bm):
62-
bm.write_src()
59+
bm.write_src('example')
6360
self.compile_src(bm)
6461

6562
def assertNoErrors(self, src):
@@ -68,7 +65,7 @@ def assertNoErrors(self, src):
6865
else:
6966
sm = SimpleModule()
7067
sm.cu.add_defn(src)
71-
bm = BuiltModule(sm, pyruntime)
68+
bm = BuiltModule(sm)
7269
self.build_module(bm)
7370
bm.cleanup()
7471
return bm
@@ -79,9 +76,9 @@ def assertFindsError(self, src, experr):
7976
else:
8077
sm = SimpleModule()
8178
sm.cu.add_defn(src)
82-
bm = BuiltModule(sm, pyruntime)
79+
bm = BuiltModule(sm)
8380
try:
84-
bm.write_src()
81+
bm.write_src('example')
8582
experr = experr.replace('$(SRCFILE)', bm.srcfile)
8683
self.compile_src(bm)
8784
except CompilationError:
@@ -127,7 +124,11 @@ def test_finding_htons_error(self):
127124
return NULL;
128125
}
129126
x2 = (int)htons((short)x1);
127+
#if PY_MAJOR_VERSION >= 3
128+
return PyLong_FromLong(x2);
129+
#else
130130
return PyInt_FromLong(x2);
131+
#endif
131132
}
132133
"""
133134
self.assertFindsError(src,

0 commit comments

Comments
 (0)