PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/cpyext/test/test_cpyext.py

https://bitbucket.org/pypy/pypy/
Python | 1004 lines | 899 code | 35 blank | 70 comment | 43 complexity | 796d1cd2c0e43df64af65c7cb35f8a74 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. import weakref
  3. import os
  4. import py, pytest
  5. from pypy import pypydir
  6. from pypy.interpreter import gateway
  7. from rpython.rtyper.lltypesystem import lltype, ll2ctypes
  8. from rpython.translator.gensupp import uniquemodulename
  9. from rpython.tool.udir import udir
  10. from pypy.module.cpyext import api
  11. from pypy.module.cpyext.state import State
  12. from pypy.module.cpyext.pyobject import Py_DecRef
  13. from rpython.tool.identity_dict import identity_dict
  14. from rpython.tool import leakfinder
  15. from rpython.rlib import rawrefcount
  16. from .support import c_compile
  17. @api.cpython_api([], api.PyObject)
  18. def PyPy_Crash1(space):
  19. 1/0
  20. @api.cpython_api([], lltype.Signed, error=-1)
  21. def PyPy_Crash2(space):
  22. 1/0
  23. class TestApi:
  24. def test_signature(self):
  25. assert 'PyModule_Check' in api.FUNCTIONS
  26. assert api.FUNCTIONS['PyModule_Check'].argtypes == [api.PyObject]
  27. def convert_sources_to_files(sources, dirname):
  28. files = []
  29. for i, source in enumerate(sources):
  30. filename = dirname / ('source_%d.c' % i)
  31. with filename.open('w') as f:
  32. f.write(str(source))
  33. files.append(filename)
  34. return files
  35. def create_so(modname, include_dirs, source_strings=None, source_files=None,
  36. compile_extra=None, link_extra=None, libraries=None):
  37. dirname = (udir/uniquemodulename('module')).ensure(dir=1)
  38. if source_strings:
  39. assert not source_files
  40. files = convert_sources_to_files(source_strings, dirname)
  41. source_files = files
  42. soname = c_compile(source_files, outputfilename=str(dirname/modname),
  43. compile_extra=compile_extra, link_extra=link_extra,
  44. include_dirs=include_dirs,
  45. libraries=libraries)
  46. return soname
  47. def compile_extension_module(space, modname, include_dirs=[],
  48. source_files=None, source_strings=None):
  49. """
  50. Build an extension module and return the filename of the resulting native
  51. code file.
  52. modname is the name of the module, possibly including dots if it is a module
  53. inside a package.
  54. Any extra keyword arguments are passed on to ExternalCompilationInfo to
  55. build the module (so specify your source with one of those).
  56. """
  57. state = space.fromcache(State)
  58. api_library = state.api_lib
  59. if sys.platform == 'win32':
  60. libraries = [api_library]
  61. # '%s' undefined; assuming extern returning int
  62. compile_extra = ["/we4013"]
  63. # prevent linking with PythonXX.lib
  64. w_maj, w_min = space.fixedview(space.sys.get('version_info'), 5)[:2]
  65. link_extra = ["/NODEFAULTLIB:Python%d%d.lib" %
  66. (space.int_w(w_maj), space.int_w(w_min))]
  67. else:
  68. libraries = []
  69. if sys.platform.startswith('linux'):
  70. compile_extra = ["-Werror", "-g", "-O0", "-Wp,-U_FORTIFY_SOURCE", "-fPIC"]
  71. link_extra = ["-g"]
  72. else:
  73. compile_extra = link_extra = None
  74. modname = modname.split('.')[-1]
  75. soname = create_so(modname,
  76. include_dirs=api.include_dirs + include_dirs,
  77. source_files=source_files,
  78. source_strings=source_strings,
  79. compile_extra=compile_extra,
  80. link_extra=link_extra,
  81. libraries=libraries)
  82. from pypy.module.imp.importing import get_so_extension
  83. pydname = soname.new(purebasename=modname, ext=get_so_extension(space))
  84. soname.rename(pydname)
  85. return str(pydname)
  86. def compile_extension_module_applevel(space, modname, include_dirs=[],
  87. source_files=None, source_strings=None):
  88. """
  89. Build an extension module and return the filename of the resulting native
  90. code file.
  91. modname is the name of the module, possibly including dots if it is a module
  92. inside a package.
  93. Any extra keyword arguments are passed on to ExternalCompilationInfo to
  94. build the module (so specify your source with one of those).
  95. """
  96. if sys.platform == 'win32':
  97. compile_extra = ["/we4013"]
  98. link_extra = ["/LIBPATH:" + os.path.join(sys.exec_prefix, 'libs')]
  99. elif sys.platform == 'darwin':
  100. compile_extra = link_extra = None
  101. pass
  102. elif sys.platform.startswith('linux'):
  103. compile_extra = [
  104. "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"]
  105. link_extra = None
  106. modname = modname.split('.')[-1]
  107. soname = create_so(modname,
  108. include_dirs=[space.include_dir] + include_dirs,
  109. source_files=source_files,
  110. source_strings=source_strings,
  111. compile_extra=compile_extra,
  112. link_extra=link_extra)
  113. from imp import get_suffixes, C_EXTENSION
  114. pydname = soname
  115. for suffix, mode, typ in get_suffixes():
  116. if typ == C_EXTENSION:
  117. pydname = soname.new(purebasename=modname, ext=suffix)
  118. soname.rename(pydname)
  119. break
  120. return str(pydname)
  121. def freeze_refcnts(self):
  122. rawrefcount._dont_free_any_more()
  123. return #ZZZ
  124. state = self.space.fromcache(RefcountState)
  125. self.frozen_refcounts = {}
  126. for w_obj, obj in state.py_objects_w2r.iteritems():
  127. self.frozen_refcounts[w_obj] = obj.c_ob_refcnt
  128. #state.print_refcounts()
  129. self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
  130. class LeakCheckingTest(object):
  131. """Base class for all cpyext tests."""
  132. spaceconfig = dict(usemodules=['cpyext', 'thread', '_rawffi', 'array',
  133. 'itertools', 'time', 'binascii',
  134. 'micronumpy', 'mmap'
  135. ])
  136. enable_leak_checking = True
  137. @staticmethod
  138. def cleanup_references(space):
  139. return #ZZZ
  140. state = space.fromcache(RefcountState)
  141. import gc; gc.collect()
  142. # Clear all lifelines, objects won't resurrect
  143. for w_obj, obj in state.lifeline_dict._dict.items():
  144. if w_obj not in state.py_objects_w2r:
  145. state.lifeline_dict.set(w_obj, None)
  146. del obj
  147. import gc; gc.collect()
  148. space.getexecutioncontext().cleanup_cpyext_state()
  149. for w_obj in state.non_heaptypes_w:
  150. Py_DecRef(space, w_obj)
  151. state.non_heaptypes_w[:] = []
  152. state.reset_borrowed_references()
  153. def check_and_print_leaks(self):
  154. rawrefcount._collect()
  155. # check for sane refcnts
  156. import gc
  157. if 1: #ZZZ not self.enable_leak_checking:
  158. leakfinder.stop_tracking_allocations(check=False)
  159. return False
  160. leaking = False
  161. state = self.space.fromcache(RefcountState)
  162. gc.collect()
  163. lost_objects_w = identity_dict()
  164. lost_objects_w.update((key, None) for key in self.frozen_refcounts.keys())
  165. for w_obj, obj in state.py_objects_w2r.iteritems():
  166. base_refcnt = self.frozen_refcounts.get(w_obj)
  167. delta = obj.c_ob_refcnt
  168. if base_refcnt is not None:
  169. delta -= base_refcnt
  170. lost_objects_w.pop(w_obj)
  171. if delta != 0:
  172. leaking = True
  173. print >>sys.stderr, "Leaking %r: %i references" % (w_obj, delta)
  174. try:
  175. weakref.ref(w_obj)
  176. except TypeError:
  177. lifeline = None
  178. else:
  179. lifeline = state.lifeline_dict.get(w_obj)
  180. if lifeline is not None:
  181. refcnt = lifeline.pyo.c_ob_refcnt
  182. if refcnt > 0:
  183. print >>sys.stderr, "\tThe object also held by C code."
  184. else:
  185. referrers_repr = []
  186. for o in gc.get_referrers(w_obj):
  187. try:
  188. repr_str = repr(o)
  189. except TypeError as e:
  190. repr_str = "%s (type of o is %s)" % (str(e), type(o))
  191. referrers_repr.append(repr_str)
  192. referrers = ", ".join(referrers_repr)
  193. print >>sys.stderr, "\tThe object is referenced by these objects:", \
  194. referrers
  195. for w_obj in lost_objects_w:
  196. print >>sys.stderr, "Lost object %r" % (w_obj, )
  197. leaking = True
  198. # the actual low-level leak checking is done by pypy.tool.leakfinder,
  199. # enabled automatically by pypy.conftest.
  200. return leaking
  201. class AppTestApi(LeakCheckingTest):
  202. def setup_class(cls):
  203. from rpython.rlib.clibffi import get_libc_name
  204. if cls.runappdirect:
  205. cls.libc = get_libc_name()
  206. else:
  207. cls.w_libc = cls.space.wrap(get_libc_name())
  208. def setup_method(self, meth):
  209. freeze_refcnts(self)
  210. def teardown_method(self, meth):
  211. self.cleanup_references(self.space)
  212. # XXX: like AppTestCpythonExtensionBase.teardown_method:
  213. # find out how to disable check_and_print_leaks() if the
  214. # test failed
  215. assert not self.check_and_print_leaks(), (
  216. "Test leaks or loses object(s). You should also check if "
  217. "the test actually passed in the first place; if it failed "
  218. "it is likely to reach this place.")
  219. @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy only test')
  220. def test_only_import(self):
  221. import cpyext
  222. @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy only test')
  223. def test_load_error(self):
  224. import cpyext
  225. raises(ImportError, cpyext.load_module, "missing.file", "foo")
  226. raises(ImportError, cpyext.load_module, self.libc, "invalid.function")
  227. def test_dllhandle(self):
  228. import sys
  229. if sys.platform != "win32" or sys.version_info < (2, 6):
  230. skip("Windows Python >= 2.6 only")
  231. assert isinstance(sys.dllhandle, int)
  232. class AppTestCpythonExtensionBase(LeakCheckingTest):
  233. def setup_class(cls):
  234. space = cls.space
  235. space.getbuiltinmodule("cpyext")
  236. # 'import os' to warm up reference counts
  237. w_import = space.builtin.getdictvalue(space, '__import__')
  238. space.call_function(w_import, space.wrap("os"))
  239. #state = cls.space.fromcache(RefcountState) ZZZ
  240. #state.non_heaptypes_w[:] = []
  241. if not cls.runappdirect:
  242. cls.w_runappdirect = space.wrap(cls.runappdirect)
  243. def setup_method(self, func):
  244. @gateway.unwrap_spec(name=str)
  245. def compile_module(space, name,
  246. w_separate_module_files=None,
  247. w_separate_module_sources=None):
  248. """
  249. Build an extension module linked against the cpyext api library.
  250. """
  251. if not space.is_none(w_separate_module_files):
  252. separate_module_files = space.listview_bytes(
  253. w_separate_module_files)
  254. assert separate_module_files is not None
  255. else:
  256. separate_module_files = []
  257. if not space.is_none(w_separate_module_sources):
  258. separate_module_sources = space.listview_bytes(
  259. w_separate_module_sources)
  260. assert separate_module_sources is not None
  261. else:
  262. separate_module_sources = []
  263. pydname = self.compile_extension_module(
  264. space, name,
  265. source_files=separate_module_files,
  266. source_strings=separate_module_sources)
  267. return space.wrap(pydname)
  268. @gateway.unwrap_spec(name=str, init='str_or_None', body=str,
  269. load_it=bool, filename='str_or_None',
  270. PY_SSIZE_T_CLEAN=bool)
  271. def import_module(space, name, init=None, body='', load_it=True,
  272. filename=None, w_include_dirs=None,
  273. PY_SSIZE_T_CLEAN=False):
  274. """
  275. init specifies the overall template of the module.
  276. if init is None, the module source will be loaded from a file in this
  277. test direcory, give a name given by the filename parameter.
  278. if filename is None, the module name will be used to construct the
  279. filename.
  280. """
  281. if w_include_dirs is None:
  282. include_dirs = []
  283. else:
  284. include_dirs = [space.str_w(s) for s in space.listview(w_include_dirs)]
  285. if init is not None:
  286. code = """
  287. %(PY_SSIZE_T_CLEAN)s
  288. #include <Python.h>
  289. /* fix for cpython 2.7 Python.h if running tests with -A
  290. since pypy compiles with -fvisibility-hidden */
  291. #undef PyMODINIT_FUNC
  292. #ifdef __GNUC__
  293. # define RPY_EXPORTED extern __attribute__((visibility("default")))
  294. #else
  295. # define RPY_EXPORTED extern __declspec(dllexport)
  296. #endif
  297. #define PyMODINIT_FUNC RPY_EXPORTED void
  298. %(body)s
  299. PyMODINIT_FUNC
  300. init%(name)s(void) {
  301. %(init)s
  302. }
  303. """ % dict(name=name, init=init, body=body,
  304. PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
  305. if PY_SSIZE_T_CLEAN else '')
  306. kwds = dict(source_strings=[code])
  307. else:
  308. assert not PY_SSIZE_T_CLEAN
  309. if filename is None:
  310. filename = name
  311. filename = py.path.local(pypydir) / 'module' \
  312. / 'cpyext'/ 'test' / (filename + ".c")
  313. kwds = dict(source_files=[filename])
  314. mod = self.compile_extension_module(space, name,
  315. include_dirs=include_dirs, **kwds)
  316. if load_it:
  317. if self.runappdirect:
  318. import imp
  319. return imp.load_dynamic(name, mod)
  320. else:
  321. api.load_extension_module(space, mod, name)
  322. self.imported_module_names.append(name)
  323. return space.getitem(
  324. space.sys.get('modules'),
  325. space.wrap(name))
  326. else:
  327. path = os.path.dirname(mod)
  328. if self.runappdirect:
  329. return path
  330. else:
  331. return space.wrap(path)
  332. @gateway.unwrap_spec(mod=str, name=str)
  333. def reimport_module(space, mod, name):
  334. if self.runappdirect:
  335. import imp
  336. return imp.load_dynamic(name, mod)
  337. else:
  338. api.load_extension_module(space, mod, name)
  339. return space.getitem(
  340. space.sys.get('modules'),
  341. space.wrap(name))
  342. @gateway.unwrap_spec(modname=str, prologue=str,
  343. more_init=str, PY_SSIZE_T_CLEAN=bool)
  344. def import_extension(space, modname, w_functions, prologue="",
  345. w_include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
  346. functions = space.unwrap(w_functions)
  347. methods_table = []
  348. codes = []
  349. for funcname, flags, code in functions:
  350. cfuncname = "%s_%s" % (modname, funcname)
  351. methods_table.append("{\"%s\", %s, %s}," %
  352. (funcname, cfuncname, flags))
  353. func_code = """
  354. static PyObject* %s(PyObject* self, PyObject* args)
  355. {
  356. %s
  357. }
  358. """ % (cfuncname, code)
  359. codes.append(func_code)
  360. body = prologue + "\n".join(codes) + """
  361. static PyMethodDef methods[] = {
  362. %s
  363. { NULL }
  364. };
  365. """ % ('\n'.join(methods_table),)
  366. init = """Py_InitModule("%s", methods);""" % (modname,)
  367. if more_init:
  368. init += more_init
  369. return import_module(space, name=modname, init=init, body=body,
  370. w_include_dirs=w_include_dirs,
  371. PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
  372. @gateway.unwrap_spec(name=str)
  373. def record_imported_module(name):
  374. """
  375. Record a module imported in a test so that it can be cleaned up in
  376. teardown before the check for leaks is done.
  377. name gives the name of the module in the space's sys.modules.
  378. """
  379. self.imported_module_names.append(name)
  380. def debug_collect(space):
  381. rawrefcount._collect()
  382. # A list of modules which the test caused to be imported (in
  383. # self.space). These will be cleaned up automatically in teardown.
  384. self.imported_module_names = []
  385. if self.runappdirect:
  386. def interp2app(func):
  387. from distutils.sysconfig import get_python_inc
  388. class FakeSpace(object):
  389. def passthrough(self, arg):
  390. return arg
  391. listview = passthrough
  392. str_w = passthrough
  393. def unwrap(self, args):
  394. try:
  395. return args.str_w(None)
  396. except:
  397. return args
  398. fake = FakeSpace()
  399. fake.include_dir = get_python_inc()
  400. fake.config = self.space.config
  401. def run(*args, **kwargs):
  402. for k in kwargs.keys():
  403. if k not in func.unwrap_spec and not k.startswith('w_'):
  404. v = kwargs.pop(k)
  405. kwargs['w_' + k] = v
  406. return func(fake, *args, **kwargs)
  407. return run
  408. def wrap(func):
  409. return func
  410. self.compile_extension_module = compile_extension_module_applevel
  411. else:
  412. interp2app = gateway.interp2app
  413. wrap = self.space.wrap
  414. self.compile_extension_module = compile_extension_module
  415. self.w_compile_module = wrap(interp2app(compile_module))
  416. self.w_import_module = wrap(interp2app(import_module))
  417. self.w_reimport_module = wrap(interp2app(reimport_module))
  418. self.w_import_extension = wrap(interp2app(import_extension))
  419. self.w_record_imported_module = wrap(interp2app(record_imported_module))
  420. self.w_here = wrap(str(py.path.local(pypydir)) + '/module/cpyext/test/')
  421. self.w_debug_collect = wrap(interp2app(debug_collect))
  422. # create the file lock before we count allocations
  423. self.space.call_method(self.space.sys.get("stdout"), "flush")
  424. freeze_refcnts(self)
  425. #self.check_and_print_leaks()
  426. def unimport_module(self, name):
  427. """
  428. Remove the named module from the space's sys.modules.
  429. """
  430. w_modules = self.space.sys.get('modules')
  431. w_name = self.space.wrap(name)
  432. self.space.delitem(w_modules, w_name)
  433. def teardown_method(self, func):
  434. for name in self.imported_module_names:
  435. self.unimport_module(name)
  436. self.cleanup_references(self.space)
  437. # XXX: find out how to disable check_and_print_leaks() if the
  438. # test failed...
  439. assert not self.check_and_print_leaks(), (
  440. "Test leaks or loses object(s). You should also check if "
  441. "the test actually passed in the first place; if it failed "
  442. "it is likely to reach this place.")
  443. class AppTestCpythonExtension(AppTestCpythonExtensionBase):
  444. def test_createmodule(self):
  445. import sys
  446. init = """
  447. if (Py_IsInitialized())
  448. Py_InitModule("foo", NULL);
  449. """
  450. self.import_module(name='foo', init=init)
  451. assert 'foo' in sys.modules
  452. def test_export_function(self):
  453. import sys
  454. init = """
  455. if (Py_IsInitialized())
  456. Py_InitModule("foo", methods);
  457. """
  458. body = """
  459. PyObject* foo_pi(PyObject* self, PyObject *args)
  460. {
  461. return PyFloat_FromDouble(3.14);
  462. }
  463. static PyMethodDef methods[] = {
  464. { "return_pi", foo_pi, METH_NOARGS },
  465. { NULL }
  466. };
  467. """
  468. module = self.import_module(name='foo', init=init, body=body)
  469. assert 'foo' in sys.modules
  470. assert 'return_pi' in dir(module)
  471. assert module.return_pi is not None
  472. assert module.return_pi() == 3.14
  473. assert module.return_pi.__module__ == 'foo'
  474. def test_export_docstring(self):
  475. import sys
  476. init = """
  477. if (Py_IsInitialized())
  478. Py_InitModule("foo", methods);
  479. """
  480. body = """
  481. PyDoc_STRVAR(foo_pi_doc, "Return pi.");
  482. PyObject* foo_pi(PyObject* self, PyObject *args)
  483. {
  484. return PyFloat_FromDouble(3.14);
  485. }
  486. static PyMethodDef methods[] ={
  487. { "return_pi", foo_pi, METH_NOARGS, foo_pi_doc },
  488. { NULL }
  489. };
  490. """
  491. module = self.import_module(name='foo', init=init, body=body)
  492. doc = module.return_pi.__doc__
  493. assert doc == "Return pi."
  494. def test_InitModule4(self):
  495. init = """
  496. PyObject *cookie = PyFloat_FromDouble(3.14);
  497. Py_InitModule4("foo", methods, "docstring",
  498. cookie, PYTHON_API_VERSION);
  499. Py_DECREF(cookie);
  500. """
  501. body = """
  502. PyObject* return_cookie(PyObject* self, PyObject *args)
  503. {
  504. if (self)
  505. {
  506. Py_INCREF(self);
  507. return self;
  508. }
  509. else
  510. Py_RETURN_FALSE;
  511. }
  512. static PyMethodDef methods[] = {
  513. { "return_cookie", return_cookie, METH_NOARGS },
  514. { NULL }
  515. };
  516. """
  517. module = self.import_module(name='foo', init=init, body=body)
  518. assert module.__doc__ == "docstring"
  519. assert module.return_cookie() == 3.14
  520. def test_load_dynamic(self):
  521. import sys
  522. init = """
  523. if (Py_IsInitialized())
  524. Py_InitModule("foo", NULL);
  525. """
  526. foo = self.import_module(name='foo', init=init)
  527. assert 'foo' in sys.modules
  528. del sys.modules['foo']
  529. import imp
  530. foo2 = imp.load_dynamic('foo', foo.__file__)
  531. assert 'foo' in sys.modules
  532. assert foo.__dict__ == foo2.__dict__
  533. def test_InitModule4_dotted(self):
  534. """
  535. If the module name passed to Py_InitModule4 includes a package, only
  536. the module name (the part after the last dot) is considered when
  537. computing the name of the module initializer function.
  538. """
  539. expected_name = "pypy.module.cpyext.test.dotted"
  540. module = self.import_module(name=expected_name, filename="dotted")
  541. assert module.__name__ == expected_name
  542. def test_InitModule4_in_package(self):
  543. """
  544. If `apple.banana` is an extension module which calls Py_InitModule4 with
  545. only "banana" as a name, the resulting module nevertheless is stored at
  546. `sys.modules["apple.banana"]`.
  547. """
  548. module = self.import_module(name="apple.banana", filename="banana")
  549. assert module.__name__ == "apple.banana"
  550. def test_recursive_package_import(self):
  551. """
  552. If `cherry.date` is an extension module which imports `apple.banana`,
  553. the latter is added to `sys.modules` for the `"apple.banana"` key.
  554. """
  555. if self.runappdirect:
  556. skip('record_imported_module not supported in runappdirect mode')
  557. # Build the extensions.
  558. banana = self.compile_module(
  559. "apple.banana", separate_module_files=[self.here + 'banana.c'])
  560. self.record_imported_module("apple.banana")
  561. date = self.compile_module(
  562. "cherry.date", separate_module_files=[self.here + 'date.c'])
  563. self.record_imported_module("cherry.date")
  564. # Set up some package state so that the extensions can actually be
  565. # imported.
  566. import sys, types, os
  567. cherry = sys.modules['cherry'] = types.ModuleType('cherry')
  568. cherry.__path__ = [os.path.dirname(date)]
  569. apple = sys.modules['apple'] = types.ModuleType('apple')
  570. apple.__path__ = [os.path.dirname(banana)]
  571. import cherry.date
  572. import apple.banana
  573. assert sys.modules['apple.banana'].__name__ == 'apple.banana'
  574. assert sys.modules['cherry.date'].__name__ == 'cherry.date'
  575. def test_modinit_func(self):
  576. """
  577. A module can use the PyMODINIT_FUNC macro to declare or define its
  578. module initializer function.
  579. """
  580. module = self.import_module(name='modinit')
  581. assert module.__name__ == 'modinit'
  582. def test_export_function2(self):
  583. import sys
  584. init = """
  585. if (Py_IsInitialized())
  586. Py_InitModule("foo", methods);
  587. """
  588. body = """
  589. static PyObject* my_objects[1];
  590. static PyObject* foo_cached_pi(PyObject* self, PyObject *args)
  591. {
  592. if (my_objects[0] == NULL) {
  593. my_objects[0] = PyFloat_FromDouble(3.14);
  594. }
  595. Py_INCREF(my_objects[0]);
  596. return my_objects[0];
  597. }
  598. static PyObject* foo_drop_pi(PyObject* self, PyObject *args)
  599. {
  600. if (my_objects[0] != NULL) {
  601. Py_DECREF(my_objects[0]);
  602. my_objects[0] = NULL;
  603. }
  604. Py_INCREF(Py_None);
  605. return Py_None;
  606. }
  607. static PyObject* foo_retinvalid(PyObject* self, PyObject *args)
  608. {
  609. return (PyObject*)0xAFFEBABE;
  610. }
  611. static PyMethodDef methods[] = {
  612. { "return_pi", foo_cached_pi, METH_NOARGS },
  613. { "drop_pi", foo_drop_pi, METH_NOARGS },
  614. { "return_invalid_pointer", foo_retinvalid, METH_NOARGS },
  615. { NULL }
  616. };
  617. """
  618. module = self.import_module(name='foo', init=init, body=body)
  619. assert module.return_pi() == 3.14
  620. print "A"
  621. module.drop_pi()
  622. print "B"
  623. module.drop_pi()
  624. print "C"
  625. assert module.return_pi() == 3.14
  626. print "D"
  627. assert module.return_pi() == 3.14
  628. print "E"
  629. module.drop_pi()
  630. skip("Hmm, how to check for the exception?")
  631. raises(api.InvalidPointerException, module.return_invalid_pointer)
  632. def test_argument(self):
  633. import sys
  634. init = """
  635. if (Py_IsInitialized())
  636. Py_InitModule("foo", methods);
  637. """
  638. body = """
  639. PyObject* foo_test(PyObject* self, PyObject *args)
  640. {
  641. PyObject *t = PyTuple_GetItem(args, 0);
  642. Py_INCREF(t);
  643. return t;
  644. }
  645. static PyMethodDef methods[] = {
  646. { "test", foo_test, METH_VARARGS },
  647. { NULL }
  648. };
  649. """
  650. module = self.import_module(name='foo', init=init, body=body)
  651. assert module.test(True, True) == True
  652. def test_exception(self):
  653. import sys
  654. init = """
  655. if (Py_IsInitialized())
  656. Py_InitModule("foo", methods);
  657. """
  658. body = """
  659. static PyObject* foo_pi(PyObject* self, PyObject *args)
  660. {
  661. PyErr_SetString(PyExc_Exception, "moo!");
  662. return NULL;
  663. }
  664. static PyMethodDef methods[] = {
  665. { "raise_exception", foo_pi, METH_NOARGS },
  666. { NULL }
  667. };
  668. """
  669. module = self.import_module(name='foo', init=init, body=body)
  670. exc = raises(Exception, module.raise_exception)
  671. if type(exc.value) is not Exception:
  672. raise exc.value
  673. assert exc.value.message == "moo!"
  674. def test_refcount(self):
  675. import sys
  676. init = """
  677. if (Py_IsInitialized())
  678. Py_InitModule("foo", methods);
  679. """
  680. body = """
  681. static PyObject* foo_pi(PyObject* self, PyObject *args)
  682. {
  683. PyObject *true_obj = Py_True;
  684. Py_ssize_t refcnt = true_obj->ob_refcnt;
  685. Py_ssize_t refcnt_after;
  686. Py_INCREF(true_obj);
  687. Py_INCREF(true_obj);
  688. PyBool_Check(true_obj);
  689. refcnt_after = true_obj->ob_refcnt;
  690. Py_DECREF(true_obj);
  691. Py_DECREF(true_obj);
  692. fprintf(stderr, "REFCNT %zd %zd\\n", refcnt, refcnt_after);
  693. return PyBool_FromLong(refcnt_after == refcnt + 2);
  694. }
  695. static PyObject* foo_bar(PyObject* self, PyObject *args)
  696. {
  697. PyObject *true_obj = Py_True;
  698. PyObject *tup = NULL;
  699. Py_ssize_t refcnt = true_obj->ob_refcnt;
  700. Py_ssize_t refcnt_after;
  701. tup = PyTuple_New(1);
  702. Py_INCREF(true_obj);
  703. if (PyTuple_SetItem(tup, 0, true_obj) < 0)
  704. return NULL;
  705. refcnt_after = true_obj->ob_refcnt;
  706. Py_DECREF(tup);
  707. fprintf(stderr, "REFCNT2 %zd %zd %zd\\n", refcnt, refcnt_after,
  708. true_obj->ob_refcnt);
  709. return PyBool_FromLong(refcnt_after == refcnt + 1 &&
  710. refcnt == true_obj->ob_refcnt);
  711. }
  712. static PyMethodDef methods[] = {
  713. { "test_refcount", foo_pi, METH_NOARGS },
  714. { "test_refcount2", foo_bar, METH_NOARGS },
  715. { NULL }
  716. };
  717. """
  718. module = self.import_module(name='foo', init=init, body=body)
  719. assert module.test_refcount()
  720. assert module.test_refcount2()
  721. def test_init_exception(self):
  722. import sys
  723. init = """
  724. PyErr_SetString(PyExc_Exception, "moo!");
  725. """
  726. exc = raises(Exception, "self.import_module(name='foo', init=init)")
  727. if type(exc.value) is not Exception:
  728. raise exc.value
  729. assert exc.value.message == "moo!"
  730. def test_internal_exceptions(self):
  731. if self.runappdirect:
  732. skip('cannot import module with undefined functions')
  733. init = """
  734. if (Py_IsInitialized())
  735. Py_InitModule("foo", methods);
  736. """
  737. body = """
  738. PyAPI_FUNC(PyObject*) PyPy_Crash1(void);
  739. PyAPI_FUNC(long) PyPy_Crash2(void);
  740. static PyObject* foo_crash1(PyObject* self, PyObject *args)
  741. {
  742. return PyPy_Crash1();
  743. }
  744. static PyObject* foo_crash2(PyObject* self, PyObject *args)
  745. {
  746. int a = PyPy_Crash2();
  747. if (a == -1)
  748. return NULL;
  749. return PyFloat_FromDouble(a);
  750. }
  751. static PyObject* foo_crash3(PyObject* self, PyObject *args)
  752. {
  753. int a = PyPy_Crash2();
  754. if (a == -1)
  755. PyErr_Clear();
  756. return PyFloat_FromDouble(a);
  757. }
  758. static PyObject* foo_crash4(PyObject* self, PyObject *args)
  759. {
  760. int a = PyPy_Crash2();
  761. return PyFloat_FromDouble(a);
  762. }
  763. static PyObject* foo_clear(PyObject* self, PyObject *args)
  764. {
  765. PyErr_Clear();
  766. return NULL;
  767. }
  768. static PyMethodDef methods[] = {
  769. { "crash1", foo_crash1, METH_NOARGS },
  770. { "crash2", foo_crash2, METH_NOARGS },
  771. { "crash3", foo_crash3, METH_NOARGS },
  772. { "crash4", foo_crash4, METH_NOARGS },
  773. { "clear", foo_clear, METH_NOARGS },
  774. { NULL }
  775. };
  776. """
  777. module = self.import_module(name='foo', init=init, body=body)
  778. # uncaught interplevel exceptions are turned into SystemError
  779. raises(SystemError, module.crash1)
  780. raises(SystemError, module.crash2)
  781. # caught exception
  782. assert module.crash3() == -1
  783. # An exception was set, but function returned a value
  784. raises(SystemError, module.crash4)
  785. # No exception set, but NULL returned
  786. raises(SystemError, module.clear)
  787. def test_new_exception(self):
  788. mod = self.import_extension('foo', [
  789. ('newexc', 'METH_VARARGS',
  790. '''
  791. char *name = PyString_AsString(PyTuple_GetItem(args, 0));
  792. return PyErr_NewException(name, PyTuple_GetItem(args, 1),
  793. PyTuple_GetItem(args, 2));
  794. '''
  795. ),
  796. ])
  797. raises(SystemError, mod.newexc, "name", Exception, {})
  798. @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy specific test')
  799. def test_hash_pointer(self):
  800. mod = self.import_extension('foo', [
  801. ('get_hash', 'METH_NOARGS',
  802. '''
  803. return PyInt_FromLong(_Py_HashPointer(Py_None));
  804. '''
  805. ),
  806. ])
  807. h = mod.get_hash()
  808. assert h != 0
  809. assert h % 4 == 0 # it's the pointer value
  810. def test_types(self):
  811. """test the presence of random types"""
  812. mod = self.import_extension('foo', [
  813. ('get_names', 'METH_NOARGS',
  814. '''
  815. /* XXX in tests, the C type is not correct */
  816. #define NAME(type) ((PyTypeObject*)&type)->tp_name
  817. return Py_BuildValue("sssss",
  818. NAME(PyCell_Type),
  819. NAME(PyModule_Type),
  820. NAME(PyProperty_Type),
  821. NAME(PyStaticMethod_Type),
  822. NAME(PyCFunction_Type)
  823. );
  824. '''
  825. ),
  826. ])
  827. assert mod.get_names() == ('cell', 'module', 'property',
  828. 'staticmethod',
  829. 'builtin_function_or_method')
  830. def test_get_programname(self):
  831. mod = self.import_extension('foo', [
  832. ('get_programname', 'METH_NOARGS',
  833. '''
  834. char* name1 = Py_GetProgramName();
  835. char* name2 = Py_GetProgramName();
  836. if (name1 != name2)
  837. Py_RETURN_FALSE;
  838. return PyString_FromString(name1);
  839. '''
  840. ),
  841. ])
  842. p = mod.get_programname()
  843. print p
  844. assert 'py' in p
  845. @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy only test')
  846. def test_get_version(self):
  847. mod = self.import_extension('foo', [
  848. ('get_version', 'METH_NOARGS',
  849. '''
  850. char* name1 = Py_GetVersion();
  851. char* name2 = Py_GetVersion();
  852. if (name1 != name2)
  853. Py_RETURN_FALSE;
  854. return PyString_FromString(name1);
  855. '''
  856. ),
  857. ])
  858. p = mod.get_version()
  859. print p
  860. assert 'PyPy' in p
  861. def test_no_double_imports(self):
  862. import sys, os
  863. try:
  864. init = """
  865. static int _imported_already = 0;
  866. FILE *f = fopen("_imported_already", "w");
  867. fprintf(f, "imported_already: %d\\n", _imported_already);
  868. fclose(f);
  869. _imported_already = 1;
  870. if (Py_IsInitialized()) {
  871. Py_InitModule("foo", NULL);
  872. }
  873. """
  874. self.import_module(name='foo', init=init)
  875. assert 'foo' in sys.modules
  876. f = open('_imported_already')
  877. data = f.read()
  878. f.close()
  879. assert data == 'imported_already: 0\n'
  880. f = open('_imported_already', 'w')
  881. f.write('not again!\n')
  882. f.close()
  883. m1 = sys.modules['foo']
  884. m2 = self.reimport_module(m1.__file__, name='foo')
  885. assert m1 is m2
  886. assert m1 is sys.modules['foo']
  887. f = open('_imported_already')
  888. data = f.read()
  889. f.close()
  890. assert data == 'not again!\n'
  891. finally:
  892. try:
  893. os.unlink('_imported_already')
  894. except OSError:
  895. pass
  896. def test_no_structmember(self):
  897. """structmember.h should not be included by default."""
  898. mod = self.import_extension('foo', [
  899. ('bar', 'METH_NOARGS',
  900. '''
  901. /* reuse a name that is #defined in structmember.h */
  902. int RO = 0; (void)RO;
  903. Py_RETURN_NONE;
  904. '''
  905. ),
  906. ])