[pypy-commit] pypy buffer-interface2: merge default into branch
mattip
pypy.commits at gmail.com
Tue Oct 4 15:16:17 EDT 2016
Author: Matti Picus <matti.picus at gmail.com>
Branch: buffer-interface2
Changeset: r87582:ed7457dceb76
Date: 2016-10-04 20:33 +0300
http://bitbucket.org/pypy/pypy/changeset/ed7457dceb76/
Log: merge default into branch
diff too long, truncating to 2000 out of 3661 lines
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -43,6 +43,7 @@
try:
if detect_cpu.autodetect().startswith('x86'):
working_modules.add('_vmprof')
+ working_modules.add('faulthandler')
except detect_cpu.ProcessorAutodetectError:
pass
@@ -89,6 +90,7 @@
('objspace.usemodules.thread', True)],
'cpyext': [('objspace.usemodules.array', True)],
'cppyy': [('objspace.usemodules.cpyext', True)],
+ 'faulthandler': [('objspace.usemodules._vmprof', True)],
}
module_suggests = {
# the reason you want _rawffi is for ctypes, which
@@ -114,7 +116,8 @@
"_hashlib" : ["pypy.module._ssl.interp_ssl"],
"_minimal_curses": ["pypy.module._minimal_curses.fficurses"],
"_continuation": ["rpython.rlib.rstacklet"],
- "_vmprof" : ["pypy.module._vmprof.interp_vmprof"],
+ "_vmprof" : ["pypy.module._vmprof.interp_vmprof"],
+ "faulthandler" : ["pypy.module._vmprof.interp_vmprof"],
}
def get_module_validator(modname):
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -33,7 +33,7 @@
from somewhere else. You no longer have to do the same with the
``pypy`` executable, as long as it finds its ``libpypy-c.so`` library.
-.. branch: _warning
+.. branch: _warnings
CPython allows warning.warn(('something', 1), Warning), on PyPy this
produced a "expected a readable buffer object" error. Test and fix.
@@ -42,3 +42,16 @@
CPython rejects 'a'.strip(buffer(' ')); only None, str or unicode are
allowed as arguments. Test and fix for str and unicode
+
+.. branch: faulthandler
+
+Port the 'faulthandler' module to PyPy default. This module is standard
+in Python 3.3 but can also be installed from CPython >= 2.6 from PyPI.
+
+.. branch: test-cpyext
+
+Refactor cpyext testing to be more pypy3-friendly.
+
+.. branch: better-error-missing-self
+
+Improve the error message when the user forgot the "self" argument of a method.
diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py
--- a/pypy/interpreter/app_main.py
+++ b/pypy/interpreter/app_main.py
@@ -33,6 +33,7 @@
--info : print translation information about this PyPy executable
-X track-resources : track the creation of files and sockets and display
a warning if they are not closed explicitly
+-X faulthandler : attempt to display tracebacks when PyPy crashes
"""
# Missing vs CPython: PYTHONHOME, PYTHONCASEOK
USAGE2 = """
@@ -233,12 +234,22 @@
import pypyjit
pypyjit.set_param(jitparam)
+def run_faulthandler():
+ if 'faulthandler' in sys.builtin_module_names:
+ import faulthandler
+ try:
+ faulthandler.enable(2) # manually set to stderr
+ except ValueError:
+ pass # ignore "2 is not a valid file descriptor"
+
def set_runtime_options(options, Xparam, *args):
if Xparam == 'track-resources':
sys.pypy_set_track_resources(True)
+ elif Xparam == 'faulthandler':
+ run_faulthandler()
else:
print >> sys.stderr, 'usage: %s -X [options]' % (get_sys_executable(),)
- print >> sys.stderr, '[options] can be: track-resources'
+ print >> sys.stderr, '[options] can be: track-resources, faulthandler'
raise SystemExit
class CommandLineError(Exception):
@@ -527,6 +538,9 @@
print >> sys.stderr, (
"Warning: pypy does not implement py3k warnings")
+ if os.getenv('PYTHONFAULTHANDLER'):
+ run_faulthandler()
+
## if not we_are_translated():
## for key in sorted(options):
## print '%40s: %s' % (key, options[key])
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -21,7 +21,8 @@
### Construction ###
def __init__(self, space, args_w, keywords=None, keywords_w=None,
- w_stararg=None, w_starstararg=None, keyword_names_w=None):
+ w_stararg=None, w_starstararg=None, keyword_names_w=None,
+ methodcall=False):
self.space = space
assert isinstance(args_w, list)
self.arguments_w = args_w
@@ -41,6 +42,9 @@
# a flag that specifies whether the JIT can unroll loops that operate
# on the keywords
self._jit_few_keywords = self.keywords is None or jit.isconstant(len(self.keywords))
+ # a flag whether this is likely a method call, which doesn't change the
+ # behaviour but produces better error messages
+ self.methodcall = methodcall
def __repr__(self):
""" NOT_RPYTHON """
@@ -207,7 +211,7 @@
starargs_w = []
scope_w[co_argcount] = self.space.newtuple(starargs_w)
elif avail > co_argcount:
- raise ArgErrCount(avail, num_kwds, signature, defaults_w, 0)
+ raise self.argerrcount(avail, num_kwds, signature, defaults_w, 0)
# if a **kwargs argument is needed, create the dict
w_kwds = None
@@ -241,7 +245,7 @@
kwds_mapping, self.keyword_names_w, self._jit_few_keywords)
else:
if co_argcount == 0:
- raise ArgErrCount(avail, num_kwds, signature, defaults_w, 0)
+ raise self.argerrcount(avail, num_kwds, signature, defaults_w, 0)
raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
kwds_mapping, self.keyword_names_w)
@@ -265,9 +269,12 @@
else:
missing += 1
if missing:
- raise ArgErrCount(avail, num_kwds, signature, defaults_w, missing)
+ raise self.argerrcount(avail, num_kwds, signature, defaults_w, missing)
-
+ def argerrcount(self, *args):
+ if self.methodcall:
+ return ArgErrCountMethod(*args)
+ return ArgErrCount(*args)
def parse_into_scope(self, w_firstarg,
scope_w, fnname, signature, defaults_w=None):
@@ -478,6 +485,22 @@
num_args)
return msg
+class ArgErrCountMethod(ArgErrCount):
+ """ A subclass of ArgErrCount that is used if the argument matching is done
+ as part of a method call, in which case more information is added to the
+ error message, if the cause of the error is likely a forgotten `self`
+ argument.
+ """
+
+ def getmsg(self):
+ msg = ArgErrCount.getmsg(self)
+ n = self.signature.num_argnames()
+ if (self.num_args == n + 1 and
+ (n == 0 or self.signature.argnames[0] != "self")):
+ msg += ". Did you forget 'self' in the function definition?"
+ return msg
+
+
class ArgErrMultipleValues(ArgErr):
def __init__(self, argname):
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1127,7 +1127,8 @@
args = Arguments(self, list(args_w))
return self.call_args(w_func, args)
- def call_valuestack(self, w_func, nargs, frame):
+ def call_valuestack(self, w_func, nargs, frame, methodcall=False):
+ # methodcall is only used for better error messages in argument.py
from pypy.interpreter.function import Function, Method, is_builtin_code
if frame.get_is_being_profiled() and is_builtin_code(w_func):
# XXX: this code is copied&pasted :-( from the slow path below
@@ -1144,13 +1145,15 @@
# reuse callable stack place for w_inst
frame.settopvalue(w_inst, nargs)
nargs += 1
+ methodcall = True
elif nargs > 0 and (
self.abstract_isinstance_w(frame.peekvalue(nargs-1), # :-(
w_func.w_class)):
w_func = w_func.w_function
if isinstance(w_func, Function):
- return w_func.funccall_valuestack(nargs, frame)
+ return w_func.funccall_valuestack(
+ nargs, frame, methodcall=methodcall)
# end of hack for performance
args = frame.make_arguments(nargs)
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -117,7 +117,8 @@
list(args_w[1:])))
return self.call_args(Arguments(self.space, list(args_w)))
- def funccall_valuestack(self, nargs, frame): # speed hack
+ def funccall_valuestack(self, nargs, frame, methodcall=False): # speed hack
+ # methodcall is only for better error messages
from pypy.interpreter import gateway
from pypy.interpreter.pycode import PyCode
@@ -164,7 +165,7 @@
args = frame.make_arguments(nargs-1)
return code.funcrun_obj(self, w_obj, args)
- args = frame.make_arguments(nargs)
+ args = frame.make_arguments(nargs, methodcall=methodcall)
return self.call_args(args)
@jit.unroll_safe
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -403,11 +403,14 @@
depth -= 1
self.valuestackdepth = finaldepth
- def make_arguments(self, nargs):
- return Arguments(self.space, self.peekvalues(nargs))
+ def make_arguments(self, nargs, methodcall=False):
+ return Arguments(
+ self.space, self.peekvalues(nargs), methodcall=methodcall)
- def argument_factory(self, arguments, keywords, keywords_w, w_star, w_starstar):
- return Arguments(self.space, arguments, keywords, keywords_w, w_star, w_starstar)
+ def argument_factory(self, arguments, keywords, keywords_w, w_star, w_starstar, methodcall=False):
+ return Arguments(
+ self.space, arguments, keywords, keywords_w, w_star,
+ w_starstar, methodcall=methodcall)
@jit.dont_look_inside
def descr__reduce__(self, space):
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import py
from pypy.interpreter.argument import (Arguments, ArgErr, ArgErrUnknownKwds,
- ArgErrMultipleValues, ArgErrCount)
+ ArgErrMultipleValues, ArgErrCount, ArgErrCountMethod)
from pypy.interpreter.signature import Signature
from pypy.interpreter.error import OperationError
@@ -573,6 +573,10 @@
s = err.getmsg()
assert s == "takes exactly 1 argument (0 given)"
+ sig = Signature(['self', 'b'], None, None)
+ err = ArgErrCount(3, 0, sig, [], 0)
+ s = err.getmsg()
+ assert s == "takes exactly 2 arguments (3 given)"
sig = Signature(['a', 'b'], None, None)
err = ArgErrCount(3, 0, sig, [], 0)
s = err.getmsg()
@@ -607,6 +611,57 @@
s = err.getmsg()
assert s == "takes at most 1 non-keyword argument (2 given)"
+ def test_missing_args_method(self):
+ # got_nargs, nkwds, expected_nargs, has_vararg, has_kwarg,
+ # defaults_w, missing_args
+ sig = Signature([], None, None)
+ err = ArgErrCountMethod(1, 0, sig, None, 0)
+ s = err.getmsg()
+ assert s == "takes no arguments (1 given). Did you forget 'self' in the function definition?"
+
+ sig = Signature(['a'], None, None)
+ err = ArgErrCountMethod(0, 0, sig, [], 1)
+ s = err.getmsg()
+ assert s == "takes exactly 1 argument (0 given)"
+
+ sig = Signature(['self', 'b'], None, None)
+ err = ArgErrCountMethod(3, 0, sig, [], 0)
+ s = err.getmsg()
+ assert s == "takes exactly 2 arguments (3 given)"
+ sig = Signature(['a', 'b'], None, None)
+ err = ArgErrCountMethod(3, 0, sig, [], 0)
+ s = err.getmsg()
+ assert s == "takes exactly 2 arguments (3 given). Did you forget 'self' in the function definition?"
+ err = ArgErrCountMethod(3, 0, sig, ['a'], 0)
+ s = err.getmsg()
+ assert s == "takes at most 2 arguments (3 given). Did you forget 'self' in the function definition?"
+
+ sig = Signature(['a', 'b'], '*', None)
+ err = ArgErrCountMethod(1, 0, sig, [], 1)
+ s = err.getmsg()
+ assert s == "takes at least 2 arguments (1 given)"
+ err = ArgErrCountMethod(0, 1, sig, ['a'], 1)
+ s = err.getmsg()
+ assert s == "takes at least 1 non-keyword argument (0 given)"
+
+ sig = Signature(['a'], None, '**')
+ err = ArgErrCountMethod(2, 1, sig, [], 0)
+ s = err.getmsg()
+ assert s == "takes exactly 1 non-keyword argument (2 given). Did you forget 'self' in the function definition?"
+ err = ArgErrCountMethod(0, 1, sig, [], 1)
+ s = err.getmsg()
+ assert s == "takes exactly 1 non-keyword argument (0 given)"
+
+ sig = Signature(['a'], '*', '**')
+ err = ArgErrCountMethod(0, 1, sig, [], 1)
+ s = err.getmsg()
+ assert s == "takes at least 1 non-keyword argument (0 given)"
+
+ sig = Signature(['a'], None, '**')
+ err = ArgErrCountMethod(2, 1, sig, ['a'], 0)
+ s = err.getmsg()
+ assert s == "takes at most 1 non-keyword argument (2 given). Did you forget 'self' in the function definition?"
+
def test_bad_type_for_star(self):
space = self.space
try:
@@ -674,6 +729,45 @@
exc = raises(TypeError, (lambda a, b, **kw: 0), a=1)
assert exc.value.message == "<lambda>() takes exactly 2 non-keyword arguments (0 given)"
+ @py.test.mark.skipif("config.option.runappdirect")
+ def test_error_message_method(self):
+ class A(object):
+ def f0():
+ pass
+ def f1(a):
+ pass
+ exc = raises(TypeError, lambda : A().f0())
+ assert exc.value.message == "f0() takes no arguments (1 given). Did you forget 'self' in the function definition?"
+ exc = raises(TypeError, lambda : A().f1(1))
+ assert exc.value.message == "f1() takes exactly 1 argument (2 given). Did you forget 'self' in the function definition?"
+ def f0():
+ pass
+ exc = raises(TypeError, f0, 1)
+ # does not contain the warning about missing self
+ assert exc.value.message == "f0() takes no arguments (1 given)"
+
+ @py.test.mark.skipif("config.option.runappdirect")
+ def test_error_message_module_function(self):
+ import operator # use repeat because it's defined at applevel
+ exc = raises(TypeError, lambda : operator.repeat(1, 2, 3))
+ # does not contain the warning about missing self
+ assert exc.value.message == "repeat() takes exactly 2 arguments (3 given)"
+
+ @py.test.mark.skipif("config.option.runappdirect")
+ def test_error_message_bound_method(self):
+ class A(object):
+ def f0():
+ pass
+ def f1(a):
+ pass
+ m0 = A().f0
+ exc = raises(TypeError, lambda : m0())
+ assert exc.value.message == "f0() takes no arguments (1 given). Did you forget 'self' in the function definition?"
+ m1 = A().f1
+ exc = raises(TypeError, lambda : m1(1))
+ assert exc.value.message == "f1() takes exactly 1 argument (2 given). Did you forget 'self' in the function definition?"
+
+
def test_unicode_keywords(self):
def f(**kwargs):
assert kwargs[u"美"] == 42
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -2,6 +2,8 @@
from pypy.interpreter.mixedmodule import MixedModule
from pypy.module.imp.importing import get_pyc_magic
+from rpython.rlib import rtime
+
class BuildersModule(MixedModule):
appleveldefs = {}
@@ -14,16 +16,11 @@
class TimeModule(MixedModule):
appleveldefs = {}
interpleveldefs = {}
- if sys.platform.startswith("linux") or 'bsd' in sys.platform:
- from pypy.module.__pypy__ import interp_time
+ if rtime.HAS_CLOCK_GETTIME:
interpleveldefs["clock_gettime"] = "interp_time.clock_gettime"
interpleveldefs["clock_getres"] = "interp_time.clock_getres"
- for name in [
- "CLOCK_REALTIME", "CLOCK_MONOTONIC", "CLOCK_MONOTONIC_RAW",
- "CLOCK_PROCESS_CPUTIME_ID", "CLOCK_THREAD_CPUTIME_ID"
- ]:
- if getattr(interp_time, name) is not None:
- interpleveldefs[name] = "space.wrap(interp_time.%s)" % name
+ for name in rtime.ALL_DEFINED_CLOCKS:
+ interpleveldefs[name] = "space.wrap(%d)" % getattr(rtime, name)
class ThreadModule(MixedModule):
diff --git a/pypy/module/__pypy__/interp_time.py b/pypy/module/__pypy__/interp_time.py
--- a/pypy/module/__pypy__/interp_time.py
+++ b/pypy/module/__pypy__/interp_time.py
@@ -4,71 +4,28 @@
from pypy.interpreter.error import exception_from_saved_errno
from pypy.interpreter.gateway import unwrap_spec
from rpython.rtyper.lltypesystem import rffi, lltype
-from rpython.rtyper.tool import rffi_platform
-from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.rlib import rtime
+from rpython.rlib.rtime import HAS_CLOCK_GETTIME
-if sys.platform == 'linux2':
- libraries = ["rt"]
-else:
- libraries = []
-
-
-class CConfig:
- _compilation_info_ = ExternalCompilationInfo(
- includes=["time.h"],
- libraries=libraries,
- )
-
- HAS_CLOCK_GETTIME = rffi_platform.Has('clock_gettime')
-
- CLOCK_REALTIME = rffi_platform.DefinedConstantInteger("CLOCK_REALTIME")
- CLOCK_MONOTONIC = rffi_platform.DefinedConstantInteger("CLOCK_MONOTONIC")
- CLOCK_MONOTONIC_RAW = rffi_platform.DefinedConstantInteger("CLOCK_MONOTONIC_RAW")
- CLOCK_PROCESS_CPUTIME_ID = rffi_platform.DefinedConstantInteger("CLOCK_PROCESS_CPUTIME_ID")
- CLOCK_THREAD_CPUTIME_ID = rffi_platform.DefinedConstantInteger("CLOCK_THREAD_CPUTIME_ID")
-
-cconfig = rffi_platform.configure(CConfig)
-
-HAS_CLOCK_GETTIME = cconfig["HAS_CLOCK_GETTIME"]
-
-CLOCK_REALTIME = cconfig["CLOCK_REALTIME"]
-CLOCK_MONOTONIC = cconfig["CLOCK_MONOTONIC"]
-CLOCK_MONOTONIC_RAW = cconfig["CLOCK_MONOTONIC_RAW"]
-CLOCK_PROCESS_CPUTIME_ID = cconfig["CLOCK_PROCESS_CPUTIME_ID"]
-CLOCK_THREAD_CPUTIME_ID = cconfig["CLOCK_THREAD_CPUTIME_ID"]
if HAS_CLOCK_GETTIME:
- #redo it for timespec
- CConfig.TIMESPEC = rffi_platform.Struct("struct timespec", [
- ("tv_sec", rffi.TIME_T),
- ("tv_nsec", rffi.LONG),
- ])
- cconfig = rffi_platform.configure(CConfig)
- TIMESPEC = cconfig['TIMESPEC']
-
- c_clock_gettime = rffi.llexternal("clock_gettime",
- [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
- compilation_info=CConfig._compilation_info_, releasegil=False,
- save_err=rffi.RFFI_SAVE_ERRNO
- )
- c_clock_getres = rffi.llexternal("clock_getres",
- [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
- compilation_info=CConfig._compilation_info_, releasegil=False,
- save_err=rffi.RFFI_SAVE_ERRNO
- )
@unwrap_spec(clk_id="c_int")
def clock_gettime(space, clk_id):
- with lltype.scoped_alloc(TIMESPEC) as tp:
- ret = c_clock_gettime(clk_id, tp)
+ with lltype.scoped_alloc(rtime.TIMESPEC) as tp:
+ ret = rtime.c_clock_gettime(clk_id, tp)
if ret != 0:
raise exception_from_saved_errno(space, space.w_IOError)
- return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
+ t = (float(rffi.getintfield(tp, 'c_tv_sec')) +
+ float(rffi.getintfield(tp, 'c_tv_nsec')) * 0.000000001)
+ return space.wrap(t)
@unwrap_spec(clk_id="c_int")
def clock_getres(space, clk_id):
- with lltype.scoped_alloc(TIMESPEC) as tp:
- ret = c_clock_getres(clk_id, tp)
+ with lltype.scoped_alloc(rtime.TIMESPEC) as tp:
+ ret = rtime.c_clock_getres(clk_id, tp)
if ret != 0:
raise exception_from_saved_errno(space, space.w_IOError)
- return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
+ t = (float(rffi.getintfield(tp, 'c_tv_sec')) +
+ float(rffi.getintfield(tp, 'c_tv_nsec')) * 0.000000001)
+ return space.wrap(t)
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -140,7 +140,11 @@
stream = dispatch_filename(streamio.open_file_as_stream)(
self.space, w_name, mode, buffering, signal_checker(self.space))
fd = stream.try_to_find_file_descriptor()
- self.check_not_dir(fd)
+ try:
+ self.check_not_dir(fd)
+ except:
+ stream.close()
+ raise
self.fdopenstream(stream, fd, mode)
def direct___enter__(self):
diff --git a/pypy/module/_file/test/test_file_extra.py b/pypy/module/_file/test/test_file_extra.py
--- a/pypy/module/_file/test/test_file_extra.py
+++ b/pypy/module/_file/test/test_file_extra.py
@@ -667,3 +667,20 @@
f2.close()
s2.close()
s1.close()
+
+ def test_close_fd_if_dir_check_fails(self):
+ from errno import EMFILE
+ for i in range(1700):
+ try:
+ open('/')
+ except IOError as e:
+ assert e.errno != EMFILE
+ else:
+ assert False
+
+ @py.test.mark.skipif("os.name != 'posix'")
+ def test_dont_close_fd_if_dir_check_fails_in_fdopen(self):
+ import posix
+ fd = posix.open('/', posix.O_RDONLY)
+ raises(IOError, posix.fdopen, fd)
+ posix.close(fd)
diff --git a/pypy/module/cpyext/test/_sre.c b/pypy/module/cpyext/test/_sre.c
--- a/pypy/module/cpyext/test/_sre.c
+++ b/pypy/module/cpyext/test/_sre.c
@@ -2608,8 +2608,8 @@
};
statichere PyTypeObject Pattern_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "_" SRE_MODULE ".SRE_Pattern",
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_" SRE_MODULE ".SRE_Pattern",
sizeof(PatternObject), sizeof(SRE_CODE),
(destructor)pattern_dealloc, /*tp_dealloc*/
0, /* tp_print */
@@ -3794,8 +3794,8 @@
};
statichere PyTypeObject Scanner_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "_" SRE_MODULE ".SRE_Scanner",
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_" SRE_MODULE ".SRE_Scanner",
sizeof(ScannerObject), 0,
(destructor)scanner_dealloc, /*tp_dealloc*/
0, /* tp_print */
diff --git a/pypy/module/cpyext/test/conftest.py b/pypy/module/cpyext/test/conftest.py
--- a/pypy/module/cpyext/test/conftest.py
+++ b/pypy/module/cpyext/test/conftest.py
@@ -2,6 +2,12 @@
import pytest
def pytest_configure(config):
+ if config.option.runappdirect:
+ import sys
+ import py
+ from pypy import pypydir
+ sys.path.append(str(py.path.local(pypydir) / 'tool' / 'cpyext'))
+ return
from pypy.tool.pytest.objspace import gettestobjspace
# For some reason (probably a ll2ctypes cache issue on linux64)
# it's necessary to run "import time" at least once before any
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -259,8 +259,7 @@
};
PyTypeObject UnicodeSubtype = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.fuu",
sizeof(UnicodeSubclassObject),
0,
@@ -318,8 +317,7 @@
};
PyTypeObject UnicodeSubtype2 = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.fuu2",
sizeof(UnicodeSubclassObject),
0,
@@ -377,8 +375,7 @@
};
PyTypeObject UnicodeSubtype3 = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.fuu3",
sizeof(UnicodeSubclassObject)
};
@@ -386,8 +383,7 @@
/* A Metatype */
PyTypeObject MetaType = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.Meta",
sizeof(PyHeapTypeObject),/*tp_basicsize*/
0, /*tp_itemsize*/
@@ -466,8 +462,7 @@
PyTypeObject InitErrType = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.InitErrType",
sizeof(PyObject),/*tp_basicsize*/
0, /*tp_itemsize*/
@@ -550,8 +545,7 @@
PyTypeObject SimplePropertyType = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.Property",
sizeof(PyObject),
0,
@@ -619,14 +613,13 @@
static PyObject *newCustom(PyObject *self, PyObject *args)
{
PyObject *obj = calloc(1, sizeof(PyObject));
- obj->ob_type = &CustomType;
+ Py_TYPE(obj) = &CustomType;
_Py_NewReference(obj);
return obj;
}
static PyTypeObject CustomType = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.Custom", /*tp_name*/
sizeof(PyObject), /*tp_size*/
0, /*tp_itemsize*/
@@ -635,8 +628,7 @@
};
static PyTypeObject TupleLike = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"foo.TupleLike", /*tp_name*/
sizeof(PyObject), /*tp_size*/
};
@@ -736,7 +728,7 @@
SimplePropertyType.tp_new = PyType_GenericNew;
InitErrType.tp_new = PyType_GenericNew;
- CustomType.ob_type = &MetaType;
+ Py_TYPE(&CustomType) = &MetaType;
if (PyType_Ready(&CustomType) < 0)
INITERROR;
diff --git a/pypy/module/cpyext/test/support.py b/pypy/module/cpyext/test/support.py
deleted file mode 100644
--- a/pypy/module/cpyext/test/support.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import os
-import py
-from sys import platform
-
-if os.name != 'nt':
- so_ext = 'so'
-else:
- so_ext = 'dll'
-
-def c_compile(cfilenames, outputfilename,
- compile_extra=None, link_extra=None,
- include_dirs=None, libraries=None, library_dirs=None):
- compile_extra = compile_extra or []
- link_extra = link_extra or []
- include_dirs = include_dirs or []
- libraries = libraries or []
- library_dirs = library_dirs or []
- if platform == 'win32':
- link_extra = link_extra + ['/DEBUG'] # generate .pdb file
- if platform == 'darwin':
- # support Fink & Darwinports
- for s in ('/sw/', '/opt/local/'):
- if (s + 'include' not in include_dirs
- and os.path.exists(s + 'include')):
- include_dirs.append(s + 'include')
- if s + 'lib' not in library_dirs and os.path.exists(s + 'lib'):
- library_dirs.append(s + 'lib')
-
- outputfilename = py.path.local(outputfilename).new(ext=so_ext)
- saved_environ = os.environ.copy()
- try:
- _build(
- cfilenames, outputfilename,
- compile_extra, link_extra,
- include_dirs, libraries, library_dirs)
- finally:
- # workaround for a distutils bugs where some env vars can
- # become longer and longer every time it is used
- for key, value in saved_environ.items():
- if os.environ.get(key) != value:
- os.environ[key] = value
- return outputfilename
-
-def _build(cfilenames, outputfilename, compile_extra, link_extra,
- include_dirs, libraries, library_dirs):
- from distutils.ccompiler import new_compiler
- from distutils import sysconfig
- compiler = new_compiler(force=1)
- sysconfig.customize_compiler(compiler) # XXX
- objects = []
- for cfile in cfilenames:
- cfile = py.path.local(cfile)
- old = cfile.dirpath().chdir()
- try:
- res = compiler.compile([cfile.basename],
- include_dirs=include_dirs, extra_preargs=compile_extra)
- assert len(res) == 1
- cobjfile = py.path.local(res[0])
- assert cobjfile.check()
- objects.append(str(cobjfile))
- finally:
- old.chdir()
-
- compiler.link_shared_object(
- objects, str(outputfilename),
- libraries=libraries,
- extra_preargs=link_extra,
- library_dirs=library_dirs)
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -1,24 +1,21 @@
import sys
import weakref
-import os
-import py, pytest
+import pytest
-from pypy import pypydir
-from pypy.interpreter import gateway
+from pypy.tool.cpyext.extbuild import (
+ SystemCompilationInfo, HERE, get_sys_info_app)
+from pypy.interpreter.gateway import unwrap_spec, interp2app
from rpython.rtyper.lltypesystem import lltype, ll2ctypes
-from rpython.translator.gensupp import uniquemodulename
-from rpython.tool.udir import udir
from pypy.module.cpyext import api
from pypy.module.cpyext.state import State
from pypy.module.cpyext.pyobject import Py_DecRef
from rpython.tool.identity_dict import identity_dict
from rpython.tool import leakfinder
from rpython.rlib import rawrefcount
+from rpython.tool.udir import udir
-from .support import c_compile
-
-only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names"
+only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names"
@api.cpython_api([], api.PyObject)
def PyPy_Crash1(space):
@@ -33,40 +30,19 @@
assert 'PyModule_Check' in api.FUNCTIONS
assert api.FUNCTIONS['PyModule_Check'].argtypes == [api.PyObject]
-def convert_sources_to_files(sources, dirname):
- files = []
- for i, source in enumerate(sources):
- filename = dirname / ('source_%d.c' % i)
- with filename.open('w') as f:
- f.write(str(source))
- files.append(filename)
- return files
-def create_so(modname, include_dirs, source_strings=None, source_files=None,
- compile_extra=None, link_extra=None, libraries=None):
- dirname = (udir/uniquemodulename('module')).ensure(dir=1)
- if source_strings:
- assert not source_files
- files = convert_sources_to_files(source_strings, dirname)
- source_files = files
- soname = c_compile(source_files, outputfilename=str(dirname/modname),
- compile_extra=compile_extra, link_extra=link_extra,
- include_dirs=include_dirs,
- libraries=libraries)
- return soname
+class SpaceCompiler(SystemCompilationInfo):
+ """Extension compiler for regular (untranslated PyPy) mode"""
+ def __init__(self, space, *args, **kwargs):
+ self.space = space
+ SystemCompilationInfo.__init__(self, *args, **kwargs)
-class SystemCompilationInfo(object):
- """Bundles all the generic information required to compile extensions.
+ def load_module(self, mod, name):
+ space = self.space
+ api.load_extension_module(space, mod, name)
+ return space.getitem(
+ space.sys.get('modules'), space.wrap(name))
- Note: here, 'system' means OS + target interpreter + test config + ...
- """
- def __init__(self, include_extra=None, compile_extra=None, link_extra=None,
- extra_libs=None, ext=None):
- self.include_extra = include_extra or []
- self.compile_extra = compile_extra
- self.link_extra = link_extra
- self.extra_libs = extra_libs
- self.ext = ext
def get_cpyext_info(space):
from pypy.module.imp.importing import get_so_extension
@@ -88,7 +64,8 @@
link_extra = ["-g"]
else:
compile_extra = link_extra = None
- return SystemCompilationInfo(
+ return SpaceCompiler(space,
+ builddir_base=udir,
include_extra=api.include_dirs,
compile_extra=compile_extra,
link_extra=link_extra,
@@ -96,59 +73,6 @@
ext=get_so_extension(space))
-def compile_extension_module(sys_info, modname, include_dirs=[],
- source_files=None, source_strings=None):
- """
- Build an extension module and return the filename of the resulting native
- code file.
-
- modname is the name of the module, possibly including dots if it is a module
- inside a package.
-
- Any extra keyword arguments are passed on to ExternalCompilationInfo to
- build the module (so specify your source with one of those).
- """
- modname = modname.split('.')[-1]
- soname = create_so(modname,
- include_dirs=sys_info.include_extra + include_dirs,
- source_files=source_files,
- source_strings=source_strings,
- compile_extra=sys_info.compile_extra,
- link_extra=sys_info.link_extra,
- libraries=sys_info.extra_libs)
- pydname = soname.new(purebasename=modname, ext=sys_info.ext)
- soname.rename(pydname)
- return str(pydname)
-
-def get_so_suffix():
- from imp import get_suffixes, C_EXTENSION
- for suffix, mode, typ in get_suffixes():
- if typ == C_EXTENSION:
- return suffix
- else:
- raise RuntimeError("This interpreter does not define a filename "
- "suffix for C extensions!")
-
-def get_sys_info_app():
- from distutils.sysconfig import get_python_inc
- if sys.platform == 'win32':
- compile_extra = ["/we4013"]
- link_extra = ["/LIBPATH:" + os.path.join(sys.exec_prefix, 'libs')]
- elif sys.platform == 'darwin':
- compile_extra = link_extra = None
- pass
- elif sys.platform.startswith('linux'):
- compile_extra = [
- "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"]
- link_extra = None
- ext = get_so_suffix()
- return SystemCompilationInfo(
- include_extra=[get_python_inc()],
- compile_extra=compile_extra,
- link_extra=link_extra,
- ext=get_so_suffix())
-
-
def freeze_refcnts(self):
rawrefcount._dont_free_any_more()
return #ZZZ
@@ -159,25 +83,9 @@
#state.print_refcounts()
self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
-class FakeSpace(object):
- """Like TinyObjSpace, but different"""
- def __init__(self, config):
- self.config = config
-
- def passthrough(self, arg):
- return arg
- listview = passthrough
- str_w = passthrough
-
- def unwrap(self, args):
- try:
- return args.str_w(None)
- except:
- return args
-
class LeakCheckingTest(object):
"""Base class for all cpyext tests."""
- spaceconfig = dict(usemodules=['cpyext', 'thread', '_rawffi', 'array',
+ spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array',
'itertools', 'time', 'binascii',
'micronumpy', 'mmap'
])
@@ -265,9 +173,12 @@
cls.w_libc = cls.space.wrap(get_libc_name())
def setup_method(self, meth):
- freeze_refcnts(self)
+ if not self.runappdirect:
+ freeze_refcnts(self)
def teardown_method(self, meth):
+ if self.runappdirect:
+ return
self.cleanup_references(self.space)
# XXX: like AppTestCpythonExtensionBase.teardown_method:
# find out how to disable check_and_print_leaks() if the
@@ -293,21 +204,82 @@
skip("Windows Python >= 2.6 only")
assert isinstance(sys.dllhandle, int)
+
+def _unwrap_include_dirs(space, w_include_dirs):
+ if w_include_dirs is None:
+ return None
+ else:
+ return [space.str_w(s) for s in space.listview(w_include_dirs)]
+
+def debug_collect(space):
+ rawrefcount._collect()
+
class AppTestCpythonExtensionBase(LeakCheckingTest):
def setup_class(cls):
space = cls.space
- space.getbuiltinmodule("cpyext")
- # 'import os' to warm up reference counts
- w_import = space.builtin.getdictvalue(space, '__import__')
- space.call_function(w_import, space.wrap("os"))
- #state = cls.space.fromcache(RefcountState) ZZZ
- #state.non_heaptypes_w[:] = []
+ cls.w_here = space.wrap(str(HERE))
+ cls.w_udir = space.wrap(str(udir))
+ cls.w_runappdirect = space.wrap(cls.runappdirect)
if not cls.runappdirect:
- cls.w_runappdirect = space.wrap(cls.runappdirect)
+ cls.sys_info = get_cpyext_info(space)
+ space.getbuiltinmodule("cpyext")
+ # 'import os' to warm up reference counts
+ w_import = space.builtin.getdictvalue(space, '__import__')
+ space.call_function(w_import, space.wrap("os"))
+ #state = cls.space.fromcache(RefcountState) ZZZ
+ #state.non_heaptypes_w[:] = []
+ cls.w_debug_collect = space.wrap(interp2app(debug_collect))
+ else:
+ def w_import_module(self, name, init=None, body='', filename=None,
+ include_dirs=None, PY_SSIZE_T_CLEAN=False):
+ from extbuild import get_sys_info_app
+ sys_info = get_sys_info_app(self.udir)
+ return sys_info.import_module(
+ name, init=init, body=body, filename=filename,
+ include_dirs=include_dirs,
+ PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
+ cls.w_import_module = w_import_module
+
+ def w_import_extension(self, modname, functions, prologue="",
+ include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
+ from extbuild import get_sys_info_app
+ sys_info = get_sys_info_app(self.udir)
+ return sys_info.import_extension(
+ modname, functions, prologue=prologue,
+ include_dirs=include_dirs, more_init=more_init,
+ PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
+ cls.w_import_extension = w_import_extension
+
+ def w_compile_module(self, name,
+ source_files=None, source_strings=None):
+ from extbuild import get_sys_info_app
+ sys_info = get_sys_info_app(self.udir)
+ return sys_info.compile_extension_module(name,
+ source_files=source_files, source_strings=source_strings)
+ cls.w_compile_module = w_compile_module
+
+ def w_load_module(self, mod, name):
+ from extbuild import get_sys_info_app
+ sys_info = get_sys_info_app(self.udir)
+ return sys_info.load_module(mod, name)
+ cls.w_load_module = w_load_module
+
+
+ def record_imported_module(self, name):
+ """
+ Record a module imported in a test so that it can be cleaned up in
+ teardown before the check for leaks is done.
+
+ name gives the name of the module in the space's sys.modules.
+ """
+ self.imported_module_names.append(name)
def setup_method(self, func):
- @gateway.unwrap_spec(name=str)
+ if self.runappdirect:
+ return
+
+ @unwrap_spec(name=str)
def compile_module(space, name,
w_source_files=None,
w_source_strings=None):
@@ -322,166 +294,54 @@
source_strings = space.listview_bytes(w_source_strings)
else:
source_strings = None
- pydname = compile_extension_module(
- self.sys_info, name,
+ pydname = self.sys_info.compile_extension_module(
+ name,
source_files=source_files,
source_strings=source_strings)
+
+ # hackish, but tests calling compile_module() always end up
+ # importing the result
+ self.record_imported_module(name)
+
return space.wrap(pydname)
- @gateway.unwrap_spec(name=str, init='str_or_None', body=str,
- load_it=bool, filename='str_or_None',
- PY_SSIZE_T_CLEAN=bool)
- def import_module(space, name, init=None, body='', load_it=True,
+ @unwrap_spec(name=str, init='str_or_None', body=str,
+ filename='str_or_None', PY_SSIZE_T_CLEAN=bool)
+ def import_module(space, name, init=None, body='',
filename=None, w_include_dirs=None,
PY_SSIZE_T_CLEAN=False):
- """
- init specifies the overall template of the module.
+ include_dirs = _unwrap_include_dirs(space, w_include_dirs)
+ w_result = self.sys_info.import_module(
+ name, init, body, filename, include_dirs, PY_SSIZE_T_CLEAN)
+ self.record_imported_module(name)
+ return w_result
- if init is None, the module source will be loaded from a file in this
- test direcory, give a name given by the filename parameter.
- if filename is None, the module name will be used to construct the
- filename.
- """
- if w_include_dirs is None:
- include_dirs = []
- else:
- include_dirs = [space.str_w(s) for s in space.listview(w_include_dirs)]
- if init is not None:
- code = """
- %(PY_SSIZE_T_CLEAN)s
- #include <Python.h>
- /* fix for cpython 2.7 Python.h if running tests with -A
- since pypy compiles with -fvisibility-hidden */
- #undef PyMODINIT_FUNC
- #ifdef __GNUC__
- # define RPY_EXPORTED extern __attribute__((visibility("default")))
- #else
- # define RPY_EXPORTED extern __declspec(dllexport)
- #endif
- #define PyMODINIT_FUNC RPY_EXPORTED void
+ @unwrap_spec(mod=str, name=str)
+ def load_module(space, mod, name):
+ return self.sys_info.load_module(mod, name)
- %(body)s
-
- PyMODINIT_FUNC
- init%(name)s(void) {
- %(init)s
- }
- """ % dict(name=name, init=init, body=body,
- PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
- if PY_SSIZE_T_CLEAN else '')
- kwds = dict(source_strings=[code])
- else:
- assert not PY_SSIZE_T_CLEAN
- if filename is None:
- filename = name
- filename = py.path.local(pypydir) / 'module' \
- / 'cpyext'/ 'test' / (filename + ".c")
- kwds = dict(source_files=[filename])
- mod = compile_extension_module(self.sys_info, name,
- include_dirs=include_dirs, **kwds)
-
- if load_it:
- if self.runappdirect:
- import imp
- return imp.load_dynamic(name, mod)
- else:
- api.load_extension_module(space, mod, name)
- self.imported_module_names.append(name)
- return space.getitem(
- space.sys.get('modules'),
- space.wrap(name))
- else:
- path = os.path.dirname(mod)
- if self.runappdirect:
- return path
- else:
- return space.wrap(path)
-
- @gateway.unwrap_spec(mod=str, name=str)
- def reimport_module(space, mod, name):
- if self.runappdirect:
- import imp
- return imp.load_dynamic(name, mod)
- else:
- api.load_extension_module(space, mod, name)
- return space.getitem(
- space.sys.get('modules'),
- space.wrap(name))
-
- @gateway.unwrap_spec(modname=str, prologue=str,
+ @unwrap_spec(modname=str, prologue=str,
more_init=str, PY_SSIZE_T_CLEAN=bool)
def import_extension(space, modname, w_functions, prologue="",
w_include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
functions = space.unwrap(w_functions)
- methods_table = []
- codes = []
- for funcname, flags, code in functions:
- cfuncname = "%s_%s" % (modname, funcname)
- methods_table.append("{\"%s\", %s, %s}," %
- (funcname, cfuncname, flags))
- func_code = """
- static PyObject* %s(PyObject* self, PyObject* args)
- {
- %s
- }
- """ % (cfuncname, code)
- codes.append(func_code)
-
- body = prologue + "\n".join(codes) + """
- static PyMethodDef methods[] = {
- %s
- { NULL }
- };
- """ % ('\n'.join(methods_table),)
- init = """Py_InitModule("%s", methods);""" % (modname,)
- if more_init:
- init += more_init
- return import_module(space, name=modname, init=init, body=body,
- w_include_dirs=w_include_dirs,
- PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
-
- @gateway.unwrap_spec(name=str)
- def record_imported_module(name):
- """
- Record a module imported in a test so that it can be cleaned up in
- teardown before the check for leaks is done.
-
- name gives the name of the module in the space's sys.modules.
- """
- self.imported_module_names.append(name)
-
- def debug_collect(space):
- rawrefcount._collect()
+ include_dirs = _unwrap_include_dirs(space, w_include_dirs)
+ w_result = self.sys_info.import_extension(
+ modname, functions, prologue, include_dirs, more_init,
+ PY_SSIZE_T_CLEAN)
+ self.record_imported_module(modname)
+ return w_result
# A list of modules which the test caused to be imported (in
# self.space). These will be cleaned up automatically in teardown.
self.imported_module_names = []
- if self.runappdirect:
- fake = FakeSpace(self.space.config)
- def interp2app(func):
- def run(*args, **kwargs):
- for k in kwargs.keys():
- if k not in func.unwrap_spec and not k.startswith('w_'):
- v = kwargs.pop(k)
- kwargs['w_' + k] = v
- return func(fake, *args, **kwargs)
- return run
- def wrap(func):
- return func
- self.sys_info = get_sys_info_app()
- else:
- interp2app = gateway.interp2app
- wrap = self.space.wrap
- self.sys_info = get_cpyext_info(self.space)
+ wrap = self.space.wrap
self.w_compile_module = wrap(interp2app(compile_module))
+ self.w_load_module = wrap(interp2app(load_module))
self.w_import_module = wrap(interp2app(import_module))
- self.w_reimport_module = wrap(interp2app(reimport_module))
self.w_import_extension = wrap(interp2app(import_extension))
- self.w_record_imported_module = wrap(interp2app(record_imported_module))
- self.w_here = wrap(str(py.path.local(pypydir)) + '/module/cpyext/test/')
- self.w_debug_collect = wrap(interp2app(debug_collect))
# create the file lock before we count allocations
self.space.call_method(self.space.sys.get("stdout"), "flush")
@@ -498,6 +358,8 @@
self.space.delitem(w_modules, w_name)
def teardown_method(self, func):
+ if self.runappdirect:
+ return
for name in self.imported_module_names:
self.unimport_module(name)
self.cleanup_references(self.space)
@@ -632,19 +494,15 @@
If `cherry.date` is an extension module which imports `apple.banana`,
the latter is added to `sys.modules` for the `"apple.banana"` key.
"""
- if self.runappdirect:
- skip('record_imported_module not supported in runappdirect mode')
+ import sys, types, os
# Build the extensions.
banana = self.compile_module(
- "apple.banana", source_files=[self.here + 'banana.c'])
- self.record_imported_module("apple.banana")
+ "apple.banana", source_files=[os.path.join(self.here, 'banana.c')])
date = self.compile_module(
- "cherry.date", source_files=[self.here + 'date.c'])
- self.record_imported_module("cherry.date")
+ "cherry.date", source_files=[os.path.join(self.here, 'date.c')])
# Set up some package state so that the extensions can actually be
# imported.
- import sys, types, os
cherry = sys.modules['cherry'] = types.ModuleType('cherry')
cherry.__path__ = [os.path.dirname(date)]
@@ -652,7 +510,6 @@
apple.__path__ = [os.path.dirname(banana)]
import cherry.date
- import apple.banana
assert sys.modules['apple.banana'].__name__ == 'apple.banana'
assert sys.modules['cherry.date'].__name__ == 'cherry.date'
@@ -989,7 +846,7 @@
f.write('not again!\n')
f.close()
m1 = sys.modules['foo']
- m2 = self.reimport_module(m1.__file__, name='foo')
+ m2 = self.load_module(m1.__file__, name='foo')
assert m1 is m2
assert m1 is sys.modules['foo']
diff --git a/pypy/module/cpyext/test/test_import.py b/pypy/module/cpyext/test/test_import.py
--- a/pypy/module/cpyext/test/test_import.py
+++ b/pypy/module/cpyext/test/test_import.py
@@ -1,6 +1,6 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
-from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rtyper.lltypesystem import rffi
class TestImport(BaseApiTest):
def test_import(self, space, api):
@@ -39,9 +39,9 @@
class AppTestImportLogic(AppTestCpythonExtensionBase):
def test_import_logic(self):
- path = self.import_module(name='test_import_module', load_it=False)
- import sys
- sys.path.append(path)
+ import sys, os
+ path = self.compile_module('test_import_module',
+ source_files=[os.path.join(self.here, 'test_import_module.c')])
+ sys.path.append(os.path.dirname(path))
import test_import_module
assert test_import_module.TEST is None
-
diff --git a/pypy/module/cpyext/test/test_intobject.py b/pypy/module/cpyext/test/test_intobject.py
--- a/pypy/module/cpyext/test/test_intobject.py
+++ b/pypy/module/cpyext/test/test_intobject.py
@@ -120,8 +120,7 @@
};
PyTypeObject Enum_Type = {
- PyObject_HEAD_INIT(0)
- /*ob_size*/ 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
/*tp_name*/ "Enum",
/*tp_basicsize*/ sizeof(EnumObject),
/*tp_itemsize*/ 0,
diff --git a/pypy/module/cpyext/test/test_pyfile.py b/pypy/module/cpyext/test/test_pyfile.py
--- a/pypy/module/cpyext/test/test_pyfile.py
+++ b/pypy/module/cpyext/test/test_pyfile.py
@@ -101,7 +101,7 @@
w_stdout = space.sys.get("stdout")
assert api.PyFile_SoftSpace(w_stdout, 1) == 0
assert api.PyFile_SoftSpace(w_stdout, 0) == 1
-
+
api.PyFile_SoftSpace(w_stdout, 1)
w_ns = space.newdict()
space.exec_("print 1,", w_ns, w_ns)
@@ -117,11 +117,9 @@
class AppTestPyFile(AppTestCpythonExtensionBase):
def setup_class(cls):
+ AppTestCpythonExtensionBase.setup_class.__func__(cls)
from rpython.tool.udir import udir
- if option.runappdirect:
- cls.w_udir = str(udir)
- else:
- cls.w_udir = cls.space.wrap(str(udir))
+ cls.w_udir = cls.space.wrap(str(udir))
def test_file_tell(self):
module = self.import_extension('foo', [
@@ -158,4 +156,3 @@
t_py = fid.tell()
assert t_c == t_py, 'after a fread, c level ftell(fp) %d but PyFile.tell() %d' % (t_c, t_py)
-
diff --git a/pypy/module/cpyext/test/test_sliceobject.py b/pypy/module/cpyext/test/test_sliceobject.py
--- a/pypy/module/cpyext/test/test_sliceobject.py
+++ b/pypy/module/cpyext/test/test_sliceobject.py
@@ -15,10 +15,10 @@
def get_indices(w_start, w_stop, w_step, length):
w_slice = space.newslice(w_start, w_stop, w_step)
values = lltype.malloc(Py_ssize_tP.TO, 4, flavor='raw')
-
- res = api.PySlice_GetIndicesEx(w_slice, 100, values,
- rffi.ptradd(values, 1),
- rffi.ptradd(values, 2),
+
+ res = api.PySlice_GetIndicesEx(w_slice, 100, values,
+ rffi.ptradd(values, 1),
+ rffi.ptradd(values, 2),
rffi.ptradd(values, 3))
assert res == 0
rv = values[0], values[1], values[2], values[3]
@@ -31,9 +31,9 @@
def get_indices(w_start, w_stop, w_step, length):
w_slice = space.newslice(w_start, w_stop, w_step)
values = lltype.malloc(Py_ssize_tP.TO, 3, flavor='raw')
-
- res = api.PySlice_GetIndices(w_slice, 100, values,
- rffi.ptradd(values, 1),
+
+ res = api.PySlice_GetIndices(w_slice, 100, values,
+ rffi.ptradd(values, 1),
rffi.ptradd(values, 2))
assert res == 0
rv = values[0], values[1], values[2]
@@ -47,7 +47,7 @@
("clone", "METH_O",
"""
PySliceObject *slice = (PySliceObject *)args;
- if (slice->ob_type != &PySlice_Type) {
+ if (Py_TYPE(slice) != &PySlice_Type) {
PyErr_SetNone(PyExc_ValueError);
return NULL;
}
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -834,8 +834,7 @@
}
PyTypeObject IntLike_Type = {
- PyObject_HEAD_INIT(0)
- /*ob_size*/ 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
/*tp_name*/ "IntLike",
/*tp_basicsize*/ sizeof(IntLikeObject),
};
@@ -930,8 +929,7 @@
}
PyTypeObject IntLike_Type = {
- PyObject_HEAD_INIT(0)
- /*ob_size*/ 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
/*tp_name*/ "IntLike",
/*tp_basicsize*/ sizeof(IntLikeObject),
};
@@ -944,8 +942,7 @@
} IntLikeObjectNoOp;
PyTypeObject IntLike_Type_NoOp = {
- PyObject_HEAD_INIT(0)
- /*ob_size*/ 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
/*tp_name*/ "IntLikeNoOp",
/*tp_basicsize*/ sizeof(IntLikeObjectNoOp),
};
diff --git a/pypy/module/faulthandler/__init__.py b/pypy/module/faulthandler/__init__.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/__init__.py
@@ -0,0 +1,38 @@
+import sys
+from pypy.interpreter.mixedmodule import MixedModule
+
+
+class Module(MixedModule):
+ appleveldefs = {
+ }
+
+ interpleveldefs = {
+ 'enable': 'handler.enable',
+ 'disable': 'handler.disable',
+ 'is_enabled': 'handler.is_enabled',
+# 'register': 'interp_faulthandler.register',
+#
+ 'dump_traceback': 'handler.dump_traceback',
+#
+ '_read_null': 'handler.read_null',
+ '_sigsegv': 'handler.sigsegv',
+ '_sigfpe': 'handler.sigfpe',
+ '_sigabrt': 'handler.sigabrt',
+ '_stack_overflow': 'handler.stack_overflow',
+ }
+
+ def setup_after_space_initialization(self):
+ """NOT_RPYTHON"""
+ if self.space.config.translation.thread:
+ self.extra_interpdef('dump_traceback_later',
+ 'handler.dump_traceback_later')
+ self.extra_interpdef('cancel_dump_traceback_later',
+ 'handler.cancel_dump_traceback_later')
+ if sys.platform != 'win32':
+ self.extra_interpdef('register', 'handler.register')
+ self.extra_interpdef('unregister', 'handler.unregister')
+
+ def shutdown(self, space):
+ from pypy.module.faulthandler import handler
+ handler.finish(space)
+ MixedModule.shutdown(self, space)
diff --git a/pypy/module/faulthandler/cintf.py b/pypy/module/faulthandler/cintf.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/cintf.py
@@ -0,0 +1,99 @@
+import py
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.translator import cdir
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
+
+
+cwd = py.path.local(__file__).dirpath()
+eci = ExternalCompilationInfo(
+ includes=[cwd.join('faulthandler.h')],
+ include_dirs=[str(cwd), cdir],
+ separate_module_files=[cwd.join('faulthandler.c')])
+
+eci_later = eci.merge(ExternalCompilationInfo(
+ pre_include_bits=['#define PYPY_FAULTHANDLER_LATER\n']))
+eci_user = eci.merge(ExternalCompilationInfo(
+ pre_include_bits=['#define PYPY_FAULTHANDLER_USER\n']))
+
+def direct_llexternal(*args, **kwargs):
+ kwargs.setdefault('_nowrapper', True)
+ kwargs.setdefault('compilation_info', eci)
+ return rffi.llexternal(*args, **kwargs)
+
+
+DUMP_CALLBACK = lltype.Ptr(lltype.FuncType(
+ [rffi.INT, rffi.SIGNEDP, lltype.Signed], lltype.Void))
+
+pypy_faulthandler_setup = direct_llexternal(
+ 'pypy_faulthandler_setup', [DUMP_CALLBACK], rffi.CCHARP)
+
+pypy_faulthandler_teardown = direct_llexternal(
+ 'pypy_faulthandler_teardown', [], lltype.Void)
+
+pypy_faulthandler_enable = direct_llexternal(
+ 'pypy_faulthandler_enable', [rffi.INT, rffi.INT], rffi.CCHARP)
+
+pypy_faulthandler_disable = direct_llexternal(
+ 'pypy_faulthandler_disable', [], lltype.Void)
+
+pypy_faulthandler_is_enabled = direct_llexternal(
+ 'pypy_faulthandler_is_enabled', [], rffi.INT)
+
+pypy_faulthandler_write = direct_llexternal(
+ 'pypy_faulthandler_write', [rffi.INT, rffi.CCHARP], lltype.Void)
+
+pypy_faulthandler_write_int = direct_llexternal(
+ 'pypy_faulthandler_write_int', [rffi.INT, lltype.Signed], lltype.Void)
+
+pypy_faulthandler_dump_traceback = direct_llexternal(
+ 'pypy_faulthandler_dump_traceback',
+ [rffi.INT, rffi.INT, llmemory.Address], lltype.Void)
+
+pypy_faulthandler_dump_traceback_later = direct_llexternal(
+ 'pypy_faulthandler_dump_traceback_later',
+ [rffi.LONGLONG, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+ compilation_info=eci_later)
+
+pypy_faulthandler_cancel_dump_traceback_later = direct_llexternal(
+ 'pypy_faulthandler_cancel_dump_traceback_later', [], lltype.Void)
+
+pypy_faulthandler_check_signum = direct_llexternal(
+ 'pypy_faulthandler_check_signum',
+ [rffi.LONG], rffi.INT,
+ compilation_info=eci_user)
+
+pypy_faulthandler_register = direct_llexternal(
+ 'pypy_faulthandler_register',
+ [rffi.INT, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+ compilation_info=eci_user)
+
+pypy_faulthandler_unregister = direct_llexternal(
+ 'pypy_faulthandler_unregister',
+ [rffi.INT], rffi.INT,
+ compilation_info=eci_user)
+
+
+# for tests...
+
+pypy_faulthandler_read_null = direct_llexternal(
+ 'pypy_faulthandler_read_null', [], lltype.Void)
+
+pypy_faulthandler_read_null_releasegil = direct_llexternal(
+ 'pypy_faulthandler_read_null', [], lltype.Void,
+ _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigsegv = direct_llexternal(
+ 'pypy_faulthandler_sigsegv', [], lltype.Void)
+
+pypy_faulthandler_sigsegv_releasegil = direct_llexternal(
+ 'pypy_faulthandler_sigsegv', [], lltype.Void,
+ _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigfpe = direct_llexternal(
+ 'pypy_faulthandler_sigfpe', [], lltype.Void)
+
+pypy_faulthandler_sigabrt = direct_llexternal(
+ 'pypy_faulthandler_sigabrt', [], lltype.Void)
+
+pypy_faulthandler_stackoverflow = direct_llexternal(
+ 'pypy_faulthandler_stackoverflow', [lltype.Float], lltype.Float)
diff --git a/pypy/module/faulthandler/dumper.py b/pypy/module/faulthandler/dumper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/dumper.py
@@ -0,0 +1,54 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import rgc
+from rpython.rlib.rvmprof import traceback
+
+from pypy.interpreter.pycode import PyCode
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write_int
+
+
+MAX_STRING_LENGTH = 500
+
+global_buf = lltype.malloc(rffi.CCHARP.TO, MAX_STRING_LENGTH, flavor='raw',
+ immortal=True, zero=True)
+
+def _dump(fd, s):
+ assert isinstance(s, str)
+ l = len(s)
+ if l >= MAX_STRING_LENGTH:
+ l = MAX_STRING_LENGTH - 1
+ i = 0
+ while i < l:
+ global_buf[i] = s[i]
+ i += 1
+ global_buf[l] = '\x00'
+ pypy_faulthandler_write(fd, global_buf)
+
+def _dump_int(fd, i):
+ pypy_faulthandler_write_int(fd, i)
+
+
+def dump_code(pycode, loc, fd):
+ if pycode is None:
+ _dump(fd, " File ???")
+ else:
+ _dump(fd, ' File "')
+ _dump(fd, pycode.co_filename)
+ _dump(fd, '" in ')
+ _dump(fd, pycode.co_name)
+ _dump(fd, ", from line ")
+ _dump_int(fd, pycode.co_firstlineno)
+ if loc == traceback.LOC_JITTED:
+ _dump(fd, " [jitted]")
+ elif loc == traceback.LOC_JITTED_INLINED:
+ _dump(fd, " [jit inlined]")
+ _dump(fd, "\n")
+
+
+ at rgc.no_collect
+def _dump_callback(fd, array_p, array_length):
+ """We are as careful as we can reasonably be here (i.e. not 100%,
+ but hopefully close enough). In particular, this is written as
+ RPython but shouldn't allocate anything.
+ """
+ traceback.walk_traceback(PyCode, dump_code, fd, array_p, array_length)
diff --git a/pypy/module/faulthandler/faulthandler.c b/pypy/module/faulthandler/faulthandler.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/faulthandler.c
@@ -0,0 +1,679 @@
+#include "faulthandler.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <math.h>
+
+#ifdef RPYTHON_LL2CTYPES
+# include "../../../rpython/rlib/rvmprof/src/rvmprof.h"
+#else
+# include "common_header.h"
+# include "structdef.h"
+# include "rvmprof.h"
+#endif
+#include "src/threadlocal.h"
+
+#define MAX_FRAME_DEPTH 100
+#define FRAME_DEPTH_N RVMPROF_TRACEBACK_ESTIMATE_N(MAX_FRAME_DEPTH)
+
+
+typedef struct sigaction _Py_sighandler_t;
+
+typedef struct {
+ const int signum;
+ volatile int enabled;
+ const char* name;
+ _Py_sighandler_t previous;
+} fault_handler_t;
+
+static struct {
+ int initialized;
+ int enabled;
+ volatile int fd, all_threads;
+ volatile pypy_faulthandler_cb_t dump_traceback;
+} fatal_error;
+
+static stack_t stack;
+
+
+static fault_handler_t faulthandler_handlers[] = {
+#ifdef SIGBUS
+ {SIGBUS, 0, "Bus error", },
+#endif
+#ifdef SIGILL
+ {SIGILL, 0, "Illegal instruction", },
+#endif
+ {SIGFPE, 0, "Floating point exception", },
+ {SIGABRT, 0, "Aborted", },
+ /* define SIGSEGV at the end to make it the default choice if searching the
+ handler fails in faulthandler_fatal_error() */
+ {SIGSEGV, 0, "Segmentation fault", }
+};
+static const int faulthandler_nsignals =
+ sizeof(faulthandler_handlers) / sizeof(fault_handler_t);
+
+RPY_EXTERN
+void pypy_faulthandler_write(int fd, const char *str)
+{
+ (void)write(fd, str, strlen(str));
+}
+
+RPY_EXTERN
+void pypy_faulthandler_write_int(int fd, long value)
+{
+ char buf[48];
+ sprintf(buf, "%ld", value);
+ pypy_faulthandler_write(fd, buf);
+}
+
+
+RPY_EXTERN
+void pypy_faulthandler_dump_traceback(int fd, int all_threads,
+ void *ucontext)
+{
+ pypy_faulthandler_cb_t fn;
+ intptr_t array_p[FRAME_DEPTH_N], array_length;
+
+ fn = fatal_error.dump_traceback;
+ if (!fn)
+ return;
+
+#ifndef RPYTHON_LL2CTYPES
+ if (all_threads && _RPython_ThreadLocals_AcquireTimeout(10000) == 0) {
+ /* This is known not to be perfectly safe against segfaults if we
+ don't hold the GIL ourselves. Too bad. I suspect that CPython
+ has issues there too.
+ */
+ struct pypy_threadlocal_s *my, *p;
+ int blankline = 0;
+ char buf[40];
+
+ my = (struct pypy_threadlocal_s *)_RPy_ThreadLocals_Get();
+ p = _RPython_ThreadLocals_Head();
+ p = _RPython_ThreadLocals_Enum(p);
+ while (p != NULL) {
+ if (blankline)
+ pypy_faulthandler_write(fd, "\n");
+ blankline = 1;
+
+ pypy_faulthandler_write(fd, my == p ? "Current thread" : "Thread");
+ sprintf(buf, " 0x%lx", (unsigned long)p->thread_ident);
+ pypy_faulthandler_write(fd, buf);
+ pypy_faulthandler_write(fd, " (most recent call first):\n");
+
+ array_length = vmprof_get_traceback(p->vmprof_tl_stack,
+ my == p ? ucontext : NULL,
+ array_p, FRAME_DEPTH_N);
+ fn(fd, array_p, array_length);
+
+ p = _RPython_ThreadLocals_Enum(p);
+ }
+ _RPython_ThreadLocals_Release();
+ }
+ else {
+ pypy_faulthandler_write(fd, "Stack (most recent call first):\n");
+ array_length = vmprof_get_traceback(NULL, ucontext,
+ array_p, FRAME_DEPTH_N);
+ fn(fd, array_p, array_length);
+ }
+#else
+ pypy_faulthandler_write(fd, "(no traceback when untranslated)\n");
+#endif
+}
+
+static void
+faulthandler_dump_traceback(int fd, int all_threads, void *ucontext)
+{
+ static volatile int reentrant = 0;
+
+ if (reentrant)
+ return;
+ reentrant = 1;
+ pypy_faulthandler_dump_traceback(fd, all_threads, ucontext);
+ reentrant = 0;
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_LATER
+#include "src/thread.h"
+static struct {
+ int fd;
+ long long microseconds;
+ int repeat, exit;
+ /* The main thread always holds this lock. It is only released when
+ faulthandler_thread() is interrupted before this thread exits, or at
+ Python exit. */
+ struct RPyOpaque_ThreadLock cancel_event;
+ /* released by child thread when joined */
+ struct RPyOpaque_ThreadLock running;
+} thread_later;
+
+static void faulthandler_thread(void)
+{
+#ifndef _WIN32
+ /* we don't want to receive any signal */
+ sigset_t set;
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+#endif
+
+ RPyLockStatus st;
+ char buf[64];
+ unsigned long hour, minutes, seconds, fraction;
+ long long t;
+
+ do {
+ st = RPyThreadAcquireLockTimed(&thread_later.cancel_event,
+ thread_later.microseconds, 0);
+ if (st == RPY_LOCK_ACQUIRED) {
+ RPyThreadReleaseLock(&thread_later.cancel_event);
+ break;
+ }
+ /* Timeout => dump traceback */
+ assert(st == RPY_LOCK_FAILURE);
+
+ /* getting to know which thread holds the GIL is not as simple
+ * as in CPython, so for now we don't */
+
+ t = thread_later.microseconds;
+ fraction = (unsigned long)(t % 1000000);
+ t /= 1000000;
+ seconds = (unsigned long)(t % 60);
+ t /= 60;
+ minutes = (unsigned long)(t % 60);
+ t /= 60;
+ hour = (unsigned long)t;
+ if (fraction == 0)
+ sprintf(buf, "Timeout (%lu:%02lu:%02lu)!\n",
+ hour, minutes, seconds);
+ else
+ sprintf(buf, "Timeout (%lu:%02lu:%02lu.%06lu)!\n",
+ hour, minutes, seconds, fraction);
+
+ pypy_faulthandler_write(thread_later.fd, buf);
+ pypy_faulthandler_dump_traceback(thread_later.fd, 1, NULL);
+
+ if (thread_later.exit)
+ _exit(1);
+ } while (thread_later.repeat);
+
+ /* The only way out */
+ RPyThreadReleaseLock(&thread_later.running);
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_dump_traceback_later(long long microseconds, int repeat,
+ int fd, int exit)
+{
+ pypy_faulthandler_cancel_dump_traceback_later();
+
+ thread_later.fd = fd;
+ thread_later.microseconds = microseconds;
+ thread_later.repeat = repeat;
+ thread_later.exit = exit;
+
+ RPyThreadAcquireLock(&thread_later.running, 1);
+
+ if (RPyThreadStart(&faulthandler_thread) == -1) {
+ RPyThreadReleaseLock(&thread_later.running);
+ return "unable to start watchdog thread";
+ }
+ return NULL;
+}
+#endif /* PYPY_FAULTHANDLER_LATER */
+
+RPY_EXTERN
+void pypy_faulthandler_cancel_dump_traceback_later(void)
+{
+#ifdef PYPY_FAULTHANDLER_LATER
+ /* Notify cancellation */
+ RPyThreadReleaseLock(&thread_later.cancel_event);
+
+ /* Wait for thread to join (or does nothing if no thread is running) */
+ RPyThreadAcquireLock(&thread_later.running, 1);
+ RPyThreadReleaseLock(&thread_later.running);
+
+ /* The main thread should always hold the cancel_event lock */
+ RPyThreadAcquireLock(&thread_later.cancel_event, 1);
+#endif /* PYPY_FAULTHANDLER_LATER */
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_USER
+typedef struct {
+ int enabled;
+ int fd;
+ int all_threads;
+ int chain;
+ _Py_sighandler_t previous;
+} user_signal_t;
+
+static user_signal_t *user_signals;
+
+#ifndef NSIG
+# if defined(_NSIG)
+# define NSIG _NSIG /* For BSD/SysV */
+# elif defined(_SIGMAX)
+# define NSIG (_SIGMAX + 1) /* For QNX */
+# elif defined(SIGMAX)
+# define NSIG (SIGMAX + 1) /* For djgpp */
+# else
+# define NSIG 64 /* Use a reasonable default value */
+# endif
+#endif
+
+static void faulthandler_user(int signum, siginfo_t *info, void *ucontext);
+
+static int
+faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
+{
+ struct sigaction action;
+ action.sa_handler = faulthandler_user;
+ sigemptyset(&action.sa_mask);
+ /* if the signal is received while the kernel is executing a system
+ call, try to restart the system call instead of interrupting it and
+ return EINTR. */
+ action.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (chain) {
+ /* do not prevent the signal from being received from within its
+ own signal handler */
+ action.sa_flags = SA_NODEFER;
+ }
+ if (stack.ss_sp != NULL) {
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
+ }
+ return sigaction(signum, &action, p_previous);
+}
+
+static void faulthandler_user(int signum, siginfo_t *info, void *ucontext)
+{
+ int save_errno;
+ user_signal_t *user = &user_signals[signum];
+
+ if (!user->enabled)
+ return;
+
+ save_errno = errno;
+ faulthandler_dump_traceback(user->fd, user->all_threads, ucontext);
+
+ if (user->chain) {
+ (void)sigaction(signum, &user->previous, NULL);
+ errno = save_errno;
+
+ /* call the previous signal handler */
+ raise(signum);
+
+ save_errno = errno;
+ (void)faulthandler_register(signum, user->chain, NULL);
+ }
+
+ errno = save_errno;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_check_signum(long signum)
+{
+ unsigned int i;
+
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ if (faulthandler_handlers[i].signum == signum) {
+ return -1;
+ }
+ }
+ if (signum < 1 || NSIG <= signum) {
+ return -2;
+ }
+ return 0;
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_register(int signum, int fd, int all_threads, int chain)
+{
+ user_signal_t *user;
+ _Py_sighandler_t previous;
+ int err;
+
+ if (user_signals == NULL) {
+ user_signals = malloc(NSIG * sizeof(user_signal_t));
+ if (user_signals == NULL)
+ return "out of memory";
+ memset(user_signals, 0, NSIG * sizeof(user_signal_t));
+ }
+
+ user = &user_signals[signum];
+ user->fd = fd;
+ user->all_threads = all_threads;
+ user->chain = chain;
+
+ if (!user->enabled) {
+ err = faulthandler_register(signum, chain, &previous);
More information about the pypy-commit
mailing list