Author: Manuel Jacob <m...@manueljacob.de> Branch: py3k Changeset: r77667:7cf68865e552 Date: 2015-05-29 01:59 +0200 http://bitbucket.org/pypy/pypy/changeset/7cf68865e552/
Log: hg merge 5acade5a80c5 This is part of a series of commits to merge default into the py3k branch. The merge is very large, so it's easier when split into smaller pieces. diff too long, truncating to 2000 out of 11603 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -420,3 +420,10 @@ the terms of the GPL license version 2 or any later version. Thus the _gdbm module, provided in the file lib_pypy/_gdbm.py, is redistributed under the terms of the GPL license as well. + +License for 'pypy/module/_vmprof/src' +-------------------------------------- + +The code is based on gperftools. You may see a copy of the License for it at + + https://code.google.com/p/gperftools/source/browse/COPYING diff --git a/lib-python/2.7/socket.py b/lib-python/2.7/socket.py --- a/lib-python/2.7/socket.py +++ b/lib-python/2.7/socket.py @@ -145,6 +145,34 @@ name = hostname return name +class RefCountingWarning(UserWarning): + pass + +def _do_reuse_or_drop(socket, methname): + try: + method = getattr(socket, methname) + except (AttributeError, TypeError): + warnings.warn("""'%s' object has no _reuse/_drop methods +{{ + You make use (or a library you are using makes use) of the internal + classes '_socketobject' and '_fileobject' in socket.py, initializing + them with custom objects. On PyPy, these custom objects need two + extra methods, _reuse() and _drop(), that maintain an explicit + reference counter. When _drop() has been called as many times as + _reuse(), then the object should be freed. + + Without these methods, you get the warning here. This is to + prevent the following situation: if your (or the library's) code + relies on reference counting for prompt closing, then on PyPy, the + __del__ method will be called later than on CPython. You can + easily end up in a situation where you open and close a lot of + (high-level) '_socketobject' or '_fileobject', but the (low-level) + custom objects will accumulate before their __del__ are called. + You quickly risk running out of file descriptors, for example. +}}""" % (socket.__class__.__name__,), RefCountingWarning, stacklevel=3) + else: + method() + _socketmethods = ( 'bind', 'connect', 'connect_ex', 'fileno', 'listen', @@ -182,19 +210,7 @@ if _sock is None: _sock = _realsocket(family, type, proto) else: - # PyPy note about refcounting: implemented with _reuse()/_drop() - # on the class '_socket.socket'. Python 3 did it differently - # with a reference counter on this class 'socket._socketobject' - # instead, but it is a less compatible change. - - # Note that a few libraries (like eventlet) poke at the - # private implementation of socket.py, passing custom - # objects to _socketobject(). These libraries need the - # following fix for use on PyPy: the custom objects need - # methods _reuse() and _drop() that maintains an explicit - # reference counter, starting at 0. When it drops back to - # zero, close() must be called. - _sock._reuse() + _do_reuse_or_drop(_sock, '_reuse') self._sock = _sock @@ -228,13 +244,13 @@ def close(self): s = self._sock self._sock = _closedsocket() - s._drop() + _do_reuse_or_drop(s, '_drop') close.__doc__ = _realsocket.close.__doc__ def accept(self): sock, addr = self._sock.accept() sockobj = _socketobject(_sock=sock) - sock._drop() # already a copy in the _socketobject() + _do_reuse_or_drop(sock, '_drop') # already a copy in the _socketobject() return sockobj, addr accept.__doc__ = _realsocket.accept.__doc__ @@ -290,14 +306,7 @@ "_close"] def __init__(self, sock, mode='rb', bufsize=-1, close=False): - # Note that a few libraries (like eventlet) poke at the - # private implementation of socket.py, passing custom - # objects to _fileobject(). These libraries need the - # following fix for use on PyPy: the custom objects need - # methods _reuse() and _drop() that maintains an explicit - # reference counter, starting at 0. When it drops back to - # zero, close() must be called. - sock._reuse() + _do_reuse_or_drop(sock, '_reuse') self._sock = sock self.mode = mode # Not actually used in this version if bufsize < 0: @@ -338,7 +347,7 @@ if self._close: s.close() else: - s._drop() + _do_reuse_or_drop(s, '_drop') def __del__(self): try: diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py --- a/lib_pypy/_ctypes/function.py +++ b/lib_pypy/_ctypes/function.py @@ -275,7 +275,11 @@ if argtypes: args = [argtype._CData_retval(argtype.from_address(arg)._buffer) for argtype, arg in zip(argtypes, args)] - return to_call(*args) + try: + return to_call(*args) + except SystemExit, e: + handle_system_exit(e) + raise return f def __call__(self, *args, **kwargs): @@ -304,7 +308,11 @@ except (UnicodeError, TypeError, ValueError) as e: raise ArgumentError(str(e)) try: - res = self.callable(*newargs) + try: + res = self.callable(*newargs) + except SystemExit, e: + handle_system_exit(e) + raise except: exc_info = sys.exc_info() traceback.print_tb(exc_info[2], file=sys.stderr) @@ -715,3 +723,22 @@ make_fastpath_subclass.memo[CFuncPtr] = CFuncPtrFast return CFuncPtrFast make_fastpath_subclass.memo = {} + + +def handle_system_exit(e): + # issue #1194: if we get SystemExit here, then exit the interpreter. + # Highly obscure imho but some people seem to depend on it. + if sys.flags.inspect: + return # Don't exit if -i flag was given. + else: + code = e.code + if isinstance(code, int): + exitcode = code + else: + f = getattr(sys, 'stderr', None) + if f is None: + f = sys.__stderr__ + print >> f, code + exitcode = 1 + + _rawffi.exit(exitcode) diff --git a/lib_pypy/_functools.py b/lib_pypy/_functools.py --- a/lib_pypy/_functools.py +++ b/lib_pypy/_functools.py @@ -32,16 +32,16 @@ partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords. """ - - def __init__(self, *args, **keywords): - if not args: - raise TypeError('__init__() takes at least 2 arguments (1 given)') - func, args = args[0], args[1:] + def __init__(*args, **keywords): + if len(args) < 2: + raise TypeError('__init__() takes at least 2 arguments (%d given)' + % len(args)) + self, func, args = args[0], args[1], args[2:] if not callable(func): raise TypeError("the first argument must be callable") self._func = func self._args = args - self._keywords = keywords or None + self._keywords = keywords def __delattr__(self, key): if key == '__dict__': @@ -61,9 +61,9 @@ return self._keywords def __call__(self, *fargs, **fkeywords): - if self.keywords is not None: - fkeywords = dict(self.keywords, **fkeywords) - return self.func(*(self.args + fargs), **fkeywords) + if self._keywords: + fkeywords = dict(self._keywords, **fkeywords) + return self._func(*(self._args + fargs), **fkeywords) def __repr__(self): cls = type(self) @@ -84,10 +84,13 @@ ('_func', '_args', '_keywords')) if len(d) == 0: d = None - return (type(self), (self.func,), - (self.func, self.args, self.keywords, d)) + return (type(self), (self._func,), + (self._func, self._args, self._keywords, d)) def __setstate__(self, state): - self._func, self._args, self._keywords, d = state + func, args, keywords, d = state if d is not None: self.__dict__.update(d) + self._func = func + self._args = args + self._keywords = keywords diff --git a/lib_pypy/_gdbm.py b/lib_pypy/_gdbm.py --- a/lib_pypy/_gdbm.py +++ b/lib_pypy/_gdbm.py @@ -1,4 +1,6 @@ import cffi, os, sys +import thread +_lock = thread.allocate_lock() ffi = cffi.FFI() ffi.cdef(''' @@ -40,6 +42,7 @@ try: verify_code = ''' + #include <stdlib.h> #include "gdbm.h" static datum pygdbm_fetch(GDBM_FILE gdbm_file, char *dptr, int dsize) { @@ -87,59 +90,71 @@ return {'dptr': ffi.new("char[]", key), 'dsize': len(key)} class gdbm(object): - ll_dbm = None + __ll_dbm = None + + # All public methods need to acquire the lock; all private methods + # assume the lock is already held. Thus public methods cannot call + # other public methods. def __init__(self, filename, iflags, mode): - res = lib.gdbm_open(filename, 0, iflags, mode, ffi.NULL) - self.size = -1 - if not res: - self._raise_from_errno() - self.ll_dbm = res + with _lock: + res = lib.gdbm_open(filename, 0, iflags, mode, ffi.NULL) + self.__size = -1 + if not res: + self.__raise_from_errno() + self.__ll_dbm = res def close(self): - if self.ll_dbm: - lib.gdbm_close(self.ll_dbm) - self.ll_dbm = None + with _lock: + if self.__ll_dbm: + lib.gdbm_close(self.__ll_dbm) + self.__ll_dbm = None - def _raise_from_errno(self): + def __raise_from_errno(self): if ffi.errno: raise error(ffi.errno, os.strerror(ffi.errno)) raise error(lib.gdbm_errno, lib.gdbm_strerror(lib.gdbm_errno)) def __len__(self): - if self.size < 0: - self.size = len(self.keys()) - return self.size + with _lock: + if self.__size < 0: + self.__size = len(self.__keys()) + return self.__size def __setitem__(self, key, value): - self._check_closed() - self._size = -1 - r = lib.gdbm_store(self.ll_dbm, _fromstr(key), _fromstr(value), - lib.GDBM_REPLACE) - if r < 0: - self._raise_from_errno() + with _lock: + self.__check_closed() + self.__size = -1 + r = lib.gdbm_store(self.__ll_dbm, _fromstr(key), _fromstr(value), + lib.GDBM_REPLACE) + if r < 0: + self.__raise_from_errno() def __delitem__(self, key): - self._check_closed() - res = lib.gdbm_delete(self.ll_dbm, _fromstr(key)) - if res < 0: - raise KeyError(key) + with _lock: + self.__check_closed() + self.__size = -1 + res = lib.gdbm_delete(self.__ll_dbm, _fromstr(key)) + if res < 0: + raise KeyError(key) def __contains__(self, key): - self._check_closed() - key = _checkstr(key) - return lib.pygdbm_exists(self.ll_dbm, key, len(key)) + with _lock: + self.__check_closed() + key = _checkstr(key) + return lib.pygdbm_exists(self.__ll_dbm, key, len(key)) has_key = __contains__ def get(self, key, default=None): - self._check_closed() - key = _checkstr(key) - drec = lib.pygdbm_fetch(self.ll_dbm, key, len(key)) - if not drec.dptr: - return default - res = bytes(ffi.buffer(drec.dptr, drec.dsize)) - lib.free(drec.dptr) - return res + with _lock: + self.__check_closed() + key = _checkstr(key) + drec = lib.pygdbm_fetch(self.__ll_dbm, key, len(key)) + if not drec.dptr: + return default + res = bytes(ffi.buffer(drec.dptr, drec.dsize)) + lib.free(drec.dptr) + return res def __getitem__(self, key): value = self.get(key) @@ -147,47 +162,55 @@ raise KeyError(key) return value - def keys(self): - self._check_closed() + def __keys(self): + self.__check_closed() l = [] - key = lib.gdbm_firstkey(self.ll_dbm) + key = lib.gdbm_firstkey(self.__ll_dbm) while key.dptr: l.append(bytes(ffi.buffer(key.dptr, key.dsize))) - nextkey = lib.gdbm_nextkey(self.ll_dbm, key) + nextkey = lib.gdbm_nextkey(self.__ll_dbm, key) lib.free(key.dptr) key = nextkey return l + def keys(self): + with _lock: + return self.__keys() + def firstkey(self): - self._check_closed() - key = lib.gdbm_firstkey(self.ll_dbm) - if key.dptr: - res = bytes(ffi.buffer(key.dptr, key.dsize)) - lib.free(key.dptr) - return res + with _lock: + self.__check_closed() + key = lib.gdbm_firstkey(self.__ll_dbm) + if key.dptr: + res = bytes(ffi.buffer(key.dptr, key.dsize)) + lib.free(key.dptr) + return res def nextkey(self, key): - self._check_closed() - key = lib.gdbm_nextkey(self.ll_dbm, _fromstr(key)) - if key.dptr: - res = bytes(ffi.buffer(key.dptr, key.dsize)) - lib.free(key.dptr) - return res + with _lock: + self.__check_closed() + key = lib.gdbm_nextkey(self.__ll_dbm, _fromstr(key)) + if key.dptr: + res = bytes(ffi.buffer(key.dptr, key.dsize)) + lib.free(key.dptr) + return res def reorganize(self): - self._check_closed() - if lib.gdbm_reorganize(self.ll_dbm) < 0: - self._raise_from_errno() + with _lock: + self.__check_closed() + if lib.gdbm_reorganize(self.__ll_dbm) < 0: + self.__raise_from_errno() - def _check_closed(self): - if not self.ll_dbm: + def __check_closed(self): + if not self.__ll_dbm: raise error(0, "GDBM object has already been closed") __del__ = close def sync(self): - self._check_closed() - lib.gdbm_sync(self.ll_dbm) + with _lock: + self.__check_closed() + lib.gdbm_sync(self.__ll_dbm) def setdefault(self, key, default=None): value = self.get(key) diff --git a/lib_pypy/greenlet.egg-info b/lib_pypy/greenlet.egg-info --- a/lib_pypy/greenlet.egg-info +++ b/lib_pypy/greenlet.egg-info @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: greenlet -Version: 0.4.5 +Version: 0.4.6 Summary: Lightweight in-process concurrent programming Home-page: https://github.com/python-greenlet/greenlet Author: Ralf Schmitt (for CPython), PyPy team diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py --- a/lib_pypy/greenlet.py +++ b/lib_pypy/greenlet.py @@ -2,7 +2,7 @@ import __pypy__ import _continuation -__version__ = "0.4.5" +__version__ = "0.4.6" # ____________________________________________________________ # Exceptions diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -39,6 +39,10 @@ "_csv", "_pypyjson", "_posixsubprocess", # "cppyy", "micronumpy" ]) +if sys.platform.startswith('linux') and sys.maxint > 2147483647: + if 0: # XXX disabled until we fix the absurd .so mess + working_modules.add('_vmprof') + translation_modules = default_modules.copy() translation_modules.update([ "fcntl", "time", "select", "signal", "_rawffi", "zlib", "struct", @@ -101,6 +105,7 @@ "_hashlib" : ["pypy.module._ssl.interp_ssl"], "_minimal_curses": ["pypy.module._minimal_curses.fficurses"], "_continuation": ["rpython.rlib.rstacklet"], + "_vmprof" : ["pypy.module._vmprof.interp_vmprof"], } def get_module_validator(modname): diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -320,6 +320,13 @@ http://bugs.python.org/issue14621, some of us believe it has no purpose in CPython either. +* You can't store non-string keys in type objects. For example:: + + class A(object): + locals()[42] = 3 + + won't work. + * ``sys.setrecursionlimit(n)`` sets the limit only approximately, by setting the usable stack space to ``n * 768`` bytes. On Linux, depending on the compiler settings, the default of 768KB is enough @@ -361,8 +368,13 @@ opposed to a dict proxy like in CPython. Mutating the dict will change the type and vice versa. For builtin types, a dictionary will be returned that cannot be changed (but still looks and behaves like a normal dictionary). + +* some functions and attributes of the ``gc`` module behave in a + slightly different way: for example, ``gc.enable`` and + ``gc.disable`` are supported, but instead of enabling and disabling + the GC, they just enable and disable the execution of finalizers. * PyPy prints a random line from past #pypy IRC topics at startup in - interactive mode. In a released version, this behaviour is supressed, but + interactive mode. In a released version, this behaviour is suppressed, but setting the environment variable PYPY_IRC_TOPIC will bring it back. Note that downstream package providers have been known to totally disable this feature. diff --git a/pypy/doc/embedding.rst b/pypy/doc/embedding.rst --- a/pypy/doc/embedding.rst +++ b/pypy/doc/embedding.rst @@ -51,6 +51,9 @@ otherwise return 0. You should really do your own error handling in the source. It'll acquire the GIL. + Note: this is meant to be called *only once* or a few times at most. See + the `more complete example`_ below. + .. function:: int pypy_execute_source_ptr(char* source, void* ptr); .. note:: Not available in PyPy <= 2.2.1 @@ -65,8 +68,9 @@ Note that this function is not thread-safe itself, so you need to guard it with a mutex. -Simple example --------------- + +Minimal example +--------------- Note that this API is a lot more minimal than say CPython C API, so at first it's obvious to think that you can't do much. However, the trick is to do @@ -78,10 +82,10 @@ .. code-block:: c - #include "include/PyPy.h" + #include "PyPy.h" #include <stdio.h> - const char source[] = "print 'hello from pypy'"; + static char source[] = "print 'hello from pypy'"; int main(void) { @@ -103,154 +107,115 @@ If we save it as ``x.c`` now, compile it and run it (on linux) with:: - fijal@hermann:/opt/pypy$ gcc -o x x.c -lpypy-c -L. - fijal@hermann:/opt/pypy$ LD_LIBRARY_PATH=. ./x + $ gcc -g -o x x.c -lpypy-c -L/opt/pypy/bin -I/opt/pypy/include + $ LD_LIBRARY_PATH=/opt/pypy/bin ./x hello from pypy -on OSX it is necessary to set the rpath of the binary if one wants to link to it:: +.. note:: If the compilation fails because of missing PyPy.h header file, + you are running PyPy <= 2.2.1. Get it here__. + +.. __: https://bitbucket.org/pypy/pypy/raw/c4cd6eca9358066571500ac82aaacfdaa3889e8c/include/PyPy.h + +On OSX it is necessary to set the rpath of the binary if one wants to link to it, +with a command like:: gcc -o x x.c -lpypy-c -L. -Wl,-rpath -Wl,@executable_path ./x hello from pypy -Worked! -.. note:: If the compilation fails because of missing PyPy.h header file, - you are running PyPy <= 2.2.1, please see the section `Missing PyPy.h`_. - -Missing PyPy.h --------------- - -.. note:: PyPy.h is in the nightly builds and goes to new PyPy releases (>2.2.1). - -For PyPy <= 2.2.1, you can download PyPy.h from PyPy repository (it has been added in commit c4cd6ec): - -.. code-block:: bash - - cd /opt/pypy/include - wget https://bitbucket.org/pypy/pypy/raw/c4cd6eca9358066571500ac82aaacfdaa3889e8c/include/PyPy.h - - -More advanced example +More complete example --------------------- .. note:: This example depends on pypy_execute_source_ptr which is not available - in PyPy <= 2.2.1. You might want to see the alternative example - below. + in PyPy <= 2.2.1. Typically we need something more to do than simply execute source. The following is a fully fledged example, please consult cffi documentation for details. It's a bit longish, but it captures a gist what can be done with the PyPy embedding interface: +.. code-block:: python + + # file "interface.py" + + import cffi + + ffi = cffi.FFI() + ffi.cdef(''' + struct API { + double (*add_numbers)(double x, double y); + }; + ''') + + # Better define callbacks at module scope, it's important to + # keep this object alive. + @ffi.callback("double (double, double)") + def add_numbers(x, y): + return x + y + + def fill_api(ptr): + global api + api = ffi.cast("struct API*", ptr) + api.add_numbers = add_numbers + .. code-block:: c - #include "include/PyPy.h" + /* C example */ + #include "PyPy.h" #include <stdio.h> - char source[] = "from cffi import FFI\n\ - ffi = FFI()\n\ - @ffi.callback('int(int)')\n\ - def func(a):\n\ - print 'Got from C %d' % a\n\ - return a * 2\n\ - ffi.cdef('int callback(int (*func)(int));')\n\ - c_func = ffi.cast('int(*)(int(*)(int))', c_argument)\n\ - c_func(func)\n\ - print 'finished the Python part'\n\ - "; + struct API { + double (*add_numbers)(double x, double y); + }; - int callback(int (*func)(int)) + struct API api; /* global var */ + + int initialize_api(void) { - printf("Calling to Python, result: %d\n", func(3)); - } - - int main() - { + static char source[] = + "import sys; sys.path.insert(0, '.'); " + "import interface; interface.fill_api(c_argument)"; int res; - void *lib, *func; rpython_startup_code(); res = pypy_setup_home("/opt/pypy/bin/libpypy-c.so", 1); if (res) { - printf("Error setting pypy home!\n"); + fprintf(stderr, "Error setting pypy home!\n"); + return -1; + } + res = pypy_execute_source_ptr(source, &api); + if (res) { + fprintf(stderr, "Error calling pypy_execute_source_ptr!\n"); + return -1; + } + return 0; + } + + int main(void) + { + if (initialize_api() < 0) return 1; - } - res = pypy_execute_source_ptr(source, (void*)callback); - if (res) { - printf("Error calling pypy_execute_source_ptr!\n"); - } - return res; + + printf("sum: %f\n", api.add_numbers(12.3, 45.6)); + + return 0; } you can compile and run it with:: - fijal@hermann:/opt/pypy$ gcc -g -o x x.c -lpypy-c -L. - fijal@hermann:/opt/pypy$ LD_LIBRARY_PATH=. ./x - Got from C 3 - Calling to Python, result: 6 - finished the Python part + $ gcc -g -o x x.c -lpypy-c -L/opt/pypy/bin -I/opt/pypy/include + $ LD_LIBRARY_PATH=/opt/pypy/bin ./x + sum: 57.900000 -As you can see, we successfully managed to call Python from C and C from -Python. Now having one callback might not be enough, so what typically happens -is that we would pass a struct full of callbacks to ``pypy_execute_source_ptr`` -and fill the structure from Python side for the future use. +As you can see, what we did is create a ``struct API`` that contains +the custom API that we need in our particular case. This struct is +filled by Python to contain a function pointer that is then called +form the C side. It is also possible to do have other function +pointers that are filled by the C side and called by the Python side, +or even non-function-pointer fields: basically, the two sides +communicate via this single C structure that defines your API. -Alternative example -------------------- - -As ``pypy_execute_source_ptr`` is not available in PyPy 2.2.1, you might want to try -an alternative approach which relies on -export-dynamic flag to the GNU linker. -The downside to this approach is that it is platform dependent. - -.. code-block:: c - - #include "include/PyPy.h" - #include <stdio.h> - - char source[] = "from cffi import FFI\n\ - ffi = FFI()\n\ - @ffi.callback('int(int)')\n\ - def func(a):\n\ - print 'Got from C %d' % a\n\ - return a * 2\n\ - ffi.cdef('int callback(int (*func)(int));')\n\ - lib = ffi.verify('int callback(int (*func)(int));')\n\ - lib.callback(func)\n\ - print 'finished the Python part'\n\ - "; - - int callback(int (*func)(int)) - { - printf("Calling to Python, result: %d\n", func(3)); - } - - int main() - { - int res; - void *lib, *func; - - rpython_startup_code(); - res = pypy_setup_home("/opt/pypy/bin/libpypy-c.so", 1); - if (res) { - printf("Error setting pypy home!\n"); - return 1; - } - res = pypy_execute_source(source); - if (res) { - printf("Error calling pypy_execute_source!\n"); - } - return res; - } - - -Make sure to pass -export-dynamic flag when compiling:: - - $ gcc -g -o x x.c -lpypy-c -L. -export-dynamic - $ LD_LIBRARY_PATH=. ./x - Got from C 3 - Calling to Python, result: 6 - finished the Python part Finding pypy_home ----------------- 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 @@ -59,6 +59,7 @@ exactly like `f(a, b)`. .. branch: issue2018 + branch issue2018: Allow prebuilt rpython dict with function values @@ -66,4 +67,50 @@ .. Merged but then backed out, hopefully it will return as vmprof2 .. branch: object-dtype2 + +branch object-dtype2: Extend numpy dtypes to allow using objects with associated garbage collection hook + +.. branch: vmprof2 + +branch vmprof2: +Add backend support for vmprof - a lightweight statistical profiler - +to linux64, see client at https://vmprof.readthedocs.org + +.. branch: jit_hint_docs + +branch jit_hint_docs: +Add more detail to @jit.elidable and @jit.promote in rpython/rlib/jit.py + +.. branch: remove-frame-debug-attrs + +branch remove_frame-debug-attrs: +Remove the debug attributes from frames only used for tracing and replace +them with a debug object that is created on-demand + +.. branch: can_cast + +branch can_cast: +Implement np.can_cast, np.min_scalar_type and missing dtype comparison operations. + +.. branch: numpy-fixes + +branch numpy-fixes: +Fix some error related to object dtype, non-contiguous arrays, inplement parts of +__array_interface__, __array_priority__, __array_wrap__ + +.. branch: cells-local-stack + +branch cells-local-stack: +Unify the PyFrame.cells and Pyframe.locals_stack_w lists, making frame objects +1 or 3 words smaller. + +.. branch: pythonoptimize-env + +branch pythonoptimize-env +Implement PYTHONOPTIMIZE environment variable, fixing issue #2044 + +.. branch: numpy-flags + +branch numpy-flags +Finish implementation of ndarray.flags, including str() and repr() diff --git a/pypy/goal/pypy.ico b/pypy/goal/pypy.ico new file mode 100644 index 0000000000000000000000000000000000000000..09d07dcc5a783200f440c68c0987926a80d6b667 GIT binary patch [cut] diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -245,6 +245,7 @@ config.translation.suggest(check_str_without_nul=True) config.translation.suggest(shared=True) + config.translation.suggest(icon=os.path.join(this_dir, 'pypy.ico')) if config.translation.shared: if config.translation.output is not None: raise Exception("Cannot use the --output option with PyPy " 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 @@ -13,7 +13,7 @@ -i : inspect interactively after running script; forces a prompt even if stdin does not appear to be a terminal; also PYTHONINSPECT=x -m mod : run library module as a script (terminates option list) --O : skip assert statements +-O : skip assert statements; also PYTHONOPTIMIZE=x -OO : remove docstrings when importing modules in addition to -O -q : don't print version and copyright messages on interactive startup -R : ignored (see http://bugs.python.org/issue14621) @@ -419,6 +419,21 @@ return function(options, funcarg, iterargv) +def parse_env(name, key, options): + ''' Modify options inplace if name exists in os.environ + ''' + import os + v = os.getenv(name) + if v: + options[key] = max(1, options[key]) + try: + newval = int(v) + except ValueError: + pass + else: + newval = max(1, newval) + options[key] = max(options[key], newval) + def parse_command_line(argv): import os options = default_options.copy() @@ -461,17 +476,15 @@ sys.argv[:] = argv if not options["ignore_environment"]: - if os.getenv('PYTHONDEBUG'): - options["debug"] = 1 + parse_env('PYTHONDEBUG', "debug", options) if os.getenv('PYTHONDONTWRITEBYTECODE'): options["dont_write_bytecode"] = 1 if os.getenv('PYTHONNOUSERSITE'): options["no_user_site"] = 1 if os.getenv('PYTHONUNBUFFERED'): options["unbuffered"] = 1 - if os.getenv('PYTHONVERBOSE'): - options["verbose"] = 1 - + parse_env('PYTHONVERBOSE', "verbose", options) + parse_env('PYTHONOPTIMIZE', "optimize", options) if (options["interactive"] or (not options["ignore_environment"] and os.getenv('PYTHONINSPECT'))): options["inspect"] = 1 diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -12,7 +12,7 @@ INT_MIN, INT_MAX, UINT_MAX, USHRT_MAX from pypy.interpreter.executioncontext import (ExecutionContext, ActionFlag, - UserDelAction) + UserDelAction, CodeUniqueIds) from pypy.interpreter.error import OperationError, new_exception_class, oefmt from pypy.interpreter.argument import Arguments from pypy.interpreter.miscutils import ThreadLocals, make_weak_value_dictionary @@ -395,6 +395,7 @@ self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module self.user_del_action = UserDelAction(self) + self.code_unique_ids = CodeUniqueIds() self._code_of_sys_exc_info = None # can be overridden to a subclass @@ -684,6 +685,16 @@ assert ec is not None return ec + def register_code_callback(self, callback): + cui = self.code_unique_ids + cui.code_callback = callback + + def register_code_object(self, pycode): + cui = self.code_unique_ids + if cui.code_callback is None: + return + cui.code_callback(self, pycode) + def _freeze_(self): return True @@ -1093,7 +1104,7 @@ def call_valuestack(self, w_func, nargs, frame): from pypy.interpreter.function import Function, Method, is_builtin_code - if frame.is_being_profiled and is_builtin_code(w_func): + if frame.get_is_being_profiled() and is_builtin_code(w_func): # XXX: this code is copied&pasted :-( from the slow path below # call_valuestack(). args = frame.make_arguments(nargs) diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py --- a/pypy/interpreter/executioncontext.py +++ b/pypy/interpreter/executioncontext.py @@ -96,7 +96,7 @@ def _c_call_return_trace(self, frame, w_func, args, event): if self.profilefunc is None: - frame.is_being_profiled = False + frame.getorcreatedebug().is_being_profiled = False else: # undo the effect of the CALL_METHOD bytecode, which would be # that even on a built-in method call like '[].append()', @@ -114,7 +114,7 @@ def c_exception_trace(self, frame, w_exc): "Profile function called upon OperationError." if self.profilefunc is None: - frame.is_being_profiled = False + frame.getorcreatedebug().is_being_profiled = False else: self._trace(frame, 'c_exception', w_exc) @@ -123,7 +123,7 @@ if self.gettrace() is not None or self.profilefunc is not None: self._trace(frame, 'call', self.space.w_None) if self.profilefunc: - frame.is_being_profiled = True + frame.getorcreatedebug().is_being_profiled = True def return_trace(self, frame, w_retval): "Trace the return from a function" @@ -145,7 +145,7 @@ Like bytecode_trace() but doesn't invoke any other events besides the trace function. """ - if (frame.w_f_trace is None or self.is_tracing or + if (frame.get_w_f_trace() is None or self.is_tracing or self.gettrace() is None): return self.run_trace_func(frame) @@ -154,8 +154,9 @@ @jit.unroll_safe def run_trace_func(self, frame): code = frame.pycode - if frame.instr_lb <= frame.last_instr < frame.instr_ub: - if frame.last_instr < frame.instr_prev_plus_one: + d = frame.getorcreatedebug() + if d.instr_lb <= frame.last_instr < d.instr_ub: + if frame.last_instr < d.instr_prev_plus_one: # We jumped backwards in the same line. self._trace(frame, 'line', self.space.w_None) else: @@ -170,7 +171,7 @@ break addr += c if c: - frame.instr_lb = addr + d.instr_lb = addr line += ord(lineno[p + 1]) p += 2 @@ -185,15 +186,15 @@ if ord(lineno[p + 1]): break p += 2 - frame.instr_ub = addr + d.instr_ub = addr else: - frame.instr_ub = sys.maxint + d.instr_ub = sys.maxint - if frame.instr_lb == frame.last_instr: # At start of line! - frame.f_lineno = line + if d.instr_lb == frame.last_instr: # At start of line! + d.f_lineno = line self._trace(frame, 'line', self.space.w_None) - frame.instr_prev_plus_one = frame.last_instr + 1 + d.instr_prev_plus_one = frame.last_instr + 1 def bytecode_trace_after_exception(self, frame): "Like bytecode_trace(), but without increasing the ticker." @@ -291,7 +292,7 @@ frame = self.gettopframe_nohidden() while frame: if is_being_profiled: - frame.is_being_profiled = True + frame.getorcreatedebug().is_being_profiled = True frame = self.getnextframe_nohidden(frame) def call_tracing(self, w_func, w_args): @@ -312,7 +313,7 @@ if event == 'call': w_callback = self.gettrace() else: - w_callback = frame.w_f_trace + w_callback = frame.get_w_f_trace() if w_callback is not None and event != "leaveframe": if operr is not None: @@ -323,15 +324,16 @@ frame.fast2locals() self.is_tracing += 1 try: + d = frame.getorcreatedebug() try: w_result = space.call_function(w_callback, space.wrap(frame), space.wrap(event), w_arg) if space.is_w(w_result, space.w_None): - frame.w_f_trace = None + d.w_f_trace = None else: - frame.w_f_trace = w_result + d.w_f_trace = w_result except: self.settrace(space.w_None) - frame.w_f_trace = None + d.w_f_trace = None raise finally: self.is_tracing -= 1 @@ -586,3 +588,11 @@ # there is no list of length n: if n is large, then the GC # will run several times while walking the list, but it will # see lower and lower memory usage, with no lower bound of n. + +class CodeUniqueIds(object): + def __init__(self): + if sys.maxint == 2147483647: + self.code_unique_id = 0 # XXX this is wrong, it won't work on 32bit + else: + self.code_unique_id = 0x7000000000000000 + self.code_callback = None diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -108,7 +108,7 @@ self) for i in funccallunrolling: if i < nargs: - new_frame.locals_stack_w[i] = args_w[i] + new_frame.locals_cells_stack_w[i] = args_w[i] return new_frame.run() elif nargs >= 1 and fast_natural_arity == Code.PASSTHROUGHARGS1: assert isinstance(code, gateway.BuiltinCodePassThroughArguments1) @@ -174,7 +174,7 @@ self) for i in xrange(nargs): w_arg = frame.peekvalue(nargs-1-i) - new_frame.locals_stack_w[i] = w_arg + new_frame.locals_cells_stack_w[i] = w_arg return new_frame.run() @@ -185,13 +185,13 @@ self) for i in xrange(nargs): w_arg = frame.peekvalue(nargs-1-i) - new_frame.locals_stack_w[i] = w_arg + new_frame.locals_cells_stack_w[i] = w_arg ndefs = len(self.defs_w) start = ndefs - defs_to_load i = nargs for j in xrange(start, ndefs): - new_frame.locals_stack_w[i] = self.defs_w[j] + new_frame.locals_cells_stack_w[i] = self.defs_w[j] i += 1 return new_frame.run() diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -71,7 +71,8 @@ "CPython-style code objects." _immutable_ = True _immutable_fields_ = ["co_consts_w[*]", "co_names_w[*]", "co_varnames[*]", - "co_freevars[*]", "co_cellvars[*]", "_args_as_cellvars[*]"] + "co_freevars[*]", "co_cellvars[*]", + "_args_as_cellvars[*]"] def __init__(self, space, argcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, @@ -104,6 +105,7 @@ self.magic = magic self._signature = cpython_code_signature(self) self._initialize() + space.register_code_object(self) def _initialize(self): if self.co_cellvars: @@ -146,6 +148,15 @@ from pypy.objspace.std.mapdict import init_mapdict_cache init_mapdict_cache(self) + cui = self.space.code_unique_ids + self._unique_id = cui.code_unique_id + cui.code_unique_id += 4 # so we have two bits that we can mark stuff + # with + + def _get_full_name(self): + return "py:%s:%d:%s" % (self.co_name, self.co_firstlineno, + self.co_filename) + def _cleanup_(self): if (self.magic == cpython_magic and '__pypy__' not in sys.builtin_module_names): @@ -222,7 +233,7 @@ # speed hack fresh_frame = jit.hint(frame, access_directly=True, fresh_virtualizable=True) - args.parse_into_scope(None, fresh_frame.locals_stack_w, func.name, + args.parse_into_scope(None, fresh_frame.locals_cells_stack_w, func.name, sig, func.defs_w, func.w_kw_defs) fresh_frame.init_cells() return frame.run() @@ -234,7 +245,7 @@ # speed hack fresh_frame = jit.hint(frame, access_directly=True, fresh_virtualizable=True) - args.parse_into_scope(w_obj, fresh_frame.locals_stack_w, func.name, + args.parse_into_scope(w_obj, fresh_frame.locals_cells_stack_w, func.name, sig, func.defs_w, func.w_kw_defs) fresh_frame.init_cells() return frame.run() diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -23,6 +23,19 @@ globals()[op] = stdlib_opcode.opmap[op] HAVE_ARGUMENT = stdlib_opcode.HAVE_ARGUMENT +class FrameDebugData(object): + """ A small object that holds debug data for tracing + """ + w_f_trace = None + instr_lb = 0 + instr_ub = 0 + instr_prev_plus_one = 0 + f_lineno = 0 # current lineno for tracing + is_being_profiled = False + w_locals = None + + def __init__(self, pycode): + self.f_lineno = pycode.co_firstlineno class PyFrame(W_Root): """Represents a frame for a regular Python function @@ -31,7 +44,8 @@ Public fields: * 'space' is the object space this frame is running in * 'code' is the PyCode object this frame runs - * 'w_locals' is the locals dictionary to use + * 'w_locals' is the locals dictionary to use, if needed, stored on a + debug object * 'w_globals' is the attached globals dictionary * 'builtin' is the attached built-in module * 'valuestack_w', 'blockstack', control the interpretation @@ -49,13 +63,25 @@ last_instr = -1 last_exception = None f_backref = jit.vref_None - w_f_trace = None - # For tracing - instr_lb = 0 - instr_ub = 0 - instr_prev_plus_one = 0 - is_being_profiled = False + escaped = False # see mark_as_escaped() + debugdata = None + + w_globals = None + pycode = None # code object executed by that frame + locals_cells_stack_w = None # the list of all locals, cells and the valuestack + valuestackdepth = 0 # number of items on valuestack + lastblock = None + + # other fields: + + # builtin - builtin cache, only if honor__builtins__ is True + # defaults to False + + # there is also self.space which is removed by the annotator + + # additionally JIT uses vable_token field that is representing + # frame current virtualizable state as seen by the JIT def __init__(self, space, code, w_globals, outer_func): if not we_are_translated(): @@ -65,12 +91,15 @@ assert isinstance(code, pycode.PyCode) self.space = space self.w_globals = w_globals - self.w_locals = None self.pycode = code - self.locals_stack_w = [None] * (code.co_nlocals + code.co_stacksize) - self.valuestackdepth = code.co_nlocals - self.lastblock = None - make_sure_not_resized(self.locals_stack_w) + ncellvars = len(code.co_cellvars) + nfreevars = len(code.co_freevars) + size = code.co_nlocals + ncellvars + nfreevars + code.co_stacksize + # the layout of this list is as follows: + # | local vars | cells | stack | + self.locals_cells_stack_w = [None] * size + self.valuestackdepth = code.co_nlocals + ncellvars + nfreevars + make_sure_not_resized(self.locals_cells_stack_w) check_nonneg(self.valuestackdepth) # if space.config.objspace.honor__builtins__: @@ -78,7 +107,32 @@ # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS. # class bodies only have CO_NEWLOCALS. self.initialize_frame_scopes(outer_func, code) - self.f_lineno = code.co_firstlineno + + def getdebug(self): + return self.debugdata + + def getorcreatedebug(self): + if self.debugdata is None: + self.debugdata = FrameDebugData(self.pycode) + return self.debugdata + + def get_w_f_trace(self): + d = self.getdebug() + if d is None: + return None + return d.w_f_trace + + def get_is_being_profiled(self): + d = self.getdebug() + if d is None: + return False + return d.is_being_profiled + + def get_w_locals(self): + d = self.getdebug() + if d is None: + return None + return d.w_locals def __repr__(self): # NOT_RPYTHON: useful in tracebacks @@ -86,6 +140,11 @@ self.__class__.__module__, self.__class__.__name__, self.pycode, self.get_last_lineno()) + def _getcell(self, varindex): + cell = self.locals_cells_stack_w[varindex + self.pycode.co_nlocals] + assert isinstance(cell, Cell) + return cell + def mark_as_escaped(self): """ Must be called on frames that are exposed to applevel, e.g. by @@ -131,8 +190,6 @@ else: return self.space.builtin - _NO_CELLS = [] - @jit.unroll_safe def initialize_frame_scopes(self, outer_func, code): # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS. @@ -142,17 +199,16 @@ flags = code.co_flags if not (flags & pycode.CO_OPTIMIZED): if flags & pycode.CO_NEWLOCALS: - self.w_locals = self.space.newdict(module=True) + self.getorcreatedebug().w_locals = self.space.newdict(module=True) else: assert self.w_globals is not None - self.w_locals = self.w_globals + self.getorcreatedebug().w_locals = self.w_globals ncellvars = len(code.co_cellvars) nfreevars = len(code.co_freevars) if not nfreevars: if not ncellvars: - self.cells = self._NO_CELLS - return # no self.cells needed - fast path + return # no cells needed - fast path elif outer_func is None: space = self.space raise OperationError(space.w_TypeError, @@ -165,11 +221,13 @@ if closure_size != nfreevars: raise ValueError("code object received a closure with " "an unexpected number of free variables") - self.cells = [None] * (ncellvars + nfreevars) + index = code.co_nlocals for i in range(ncellvars): - self.cells[i] = Cell() + self.locals_cells_stack_w[index] = Cell() + index += 1 for i in range(nfreevars): - self.cells[i + ncellvars] = outer_func.closure[i] + self.locals_cells_stack_w[index] = outer_func.closure[i] + index += 1 def run(self): """Start this frame's execution.""" @@ -233,14 +291,24 @@ # stack manipulation helpers def pushvalue(self, w_object): depth = self.valuestackdepth - self.locals_stack_w[depth] = w_object + self.locals_cells_stack_w[depth] = w_object self.valuestackdepth = depth + 1 + def _check_stack_index(self, index): + # will be completely removed by the optimizer if only used in an assert + # and if asserts are disabled + code = self.pycode + ncellvars = len(code.co_cellvars) + nfreevars = len(code.co_freevars) + stackstart = code.co_nlocals + ncellvars + nfreevars + return index >= stackstart + def popvalue(self): depth = self.valuestackdepth - 1 - assert depth >= self.pycode.co_nlocals, "pop from empty value stack" - w_object = self.locals_stack_w[depth] - self.locals_stack_w[depth] = None + assert self._check_stack_index(depth) + assert depth >= 0 + w_object = self.locals_cells_stack_w[depth] + self.locals_cells_stack_w[depth] = None self.valuestackdepth = depth return w_object @@ -266,25 +334,26 @@ def peekvalues(self, n): values_w = [None] * n base = self.valuestackdepth - n - assert base >= self.pycode.co_nlocals + assert self._check_stack_index(base) + assert base >= 0 while True: n -= 1 if n < 0: break - values_w[n] = self.locals_stack_w[base+n] + values_w[n] = self.locals_cells_stack_w[base+n] return values_w @jit.unroll_safe def dropvalues(self, n): n = hint(n, promote=True) finaldepth = self.valuestackdepth - n - assert finaldepth >= self.pycode.co_nlocals, ( - "stack underflow in dropvalues()") + assert self._check_stack_index(finaldepth) + assert finaldepth >= 0 while True: n -= 1 if n < 0: break - self.locals_stack_w[finaldepth+n] = None + self.locals_cells_stack_w[finaldepth+n] = None self.valuestackdepth = finaldepth @jit.unroll_safe @@ -311,34 +380,27 @@ # Contrast this with CPython where it's PEEK(-1). index_from_top = hint(index_from_top, promote=True) index = self.valuestackdepth + ~index_from_top - assert index >= self.pycode.co_nlocals, ( - "peek past the bottom of the stack") - return self.locals_stack_w[index] + assert self._check_stack_index(index) + assert index >= 0 + return self.locals_cells_stack_w[index] def settopvalue(self, w_object, index_from_top=0): index_from_top = hint(index_from_top, promote=True) index = self.valuestackdepth + ~index_from_top - assert index >= self.pycode.co_nlocals, ( - "settop past the bottom of the stack") - self.locals_stack_w[index] = w_object + assert self._check_stack_index(index) + assert index >= 0 + self.locals_cells_stack_w[index] = w_object @jit.unroll_safe def dropvaluesuntil(self, finaldepth): depth = self.valuestackdepth - 1 finaldepth = hint(finaldepth, promote=True) + assert finaldepth >= 0 while depth >= finaldepth: - self.locals_stack_w[depth] = None + self.locals_cells_stack_w[depth] = None depth -= 1 self.valuestackdepth = finaldepth - def save_locals_stack(self): - return self.locals_stack_w[:self.valuestackdepth] - - def restore_locals_stack(self, items_w): - self.locals_stack_w[:len(items_w)] = items_w - self.init_cells() - self.dropvaluesuntil(len(items_w)) - def make_arguments(self, nargs): return Arguments(self.space, self.peekvalues(nargs)) @@ -361,24 +423,16 @@ w = space.wrap nt = space.newtuple - cells = self.cells - if cells is None: - w_cells = space.w_None - else: - w_cells = space.newlist([space.wrap(cell) for cell in cells]) - - if self.w_f_trace is None: + if self.get_w_f_trace() is None: f_lineno = self.get_last_lineno() else: - f_lineno = self.f_lineno + f_lineno = self.getorcreatedebug().f_lineno nlocals = self.pycode.co_nlocals - values_w = self.locals_stack_w[nlocals:self.valuestackdepth] - w_valuestack = maker.slp_into_tuple_with_nulls(space, values_w) + values_w = self.locals_cells_stack_w + w_locals_cells_stack = maker.slp_into_tuple_with_nulls(space, values_w) w_blockstack = nt([block._get_state_(space) for block in self.get_blocklist()]) - w_fastlocals = maker.slp_into_tuple_with_nulls( - space, self.locals_stack_w[:nlocals]) if self.last_exception is None: w_exc_value = space.w_None w_tb = space.w_None @@ -386,11 +440,12 @@ w_exc_value = self.last_exception.get_w_value(space) w_tb = w(self.last_exception.get_traceback()) + d = self.getorcreatedebug() tup_state = [ w(self.f_backref()), w(self.get_builtin()), w(self.pycode), - w_valuestack, + w_locals_cells_stack, w_blockstack, w_exc_value, # last_exception w_tb, # @@ -398,16 +453,15 @@ w(self.last_instr), w(self.frame_finished_execution), w(f_lineno), - w_fastlocals, space.w_None, #XXX placeholder for f_locals #f_restricted requires no additional data! - space.w_None, ## self.w_f_trace, ignore for now + space.w_None, - w(self.instr_lb), #do we need these three (that are for tracing) - w(self.instr_ub), - w(self.instr_prev_plus_one), - w_cells, + w(d.instr_lb), + w(d.instr_ub), + w(d.instr_prev_plus_one), + w(self.valuestackdepth), ] return nt(tup_state) @@ -416,24 +470,20 @@ from pypy.module._pickle_support import maker # helper fns from pypy.interpreter.pycode import PyCode from pypy.interpreter.module import Module - args_w = space.unpackiterable(w_args, 18) - w_f_back, w_builtin, w_pycode, w_valuestack, w_blockstack, w_exc_value, w_tb,\ - w_globals, w_last_instr, w_finished, w_f_lineno, w_fastlocals, w_f_locals, \ - w_f_trace, w_instr_lb, w_instr_ub, w_instr_prev_plus_one, w_cells = args_w + args_w = space.unpackiterable(w_args, 17) + w_f_back, w_builtin, w_pycode, w_locals_cells_stack, w_blockstack, w_exc_value, w_tb,\ + w_globals, w_last_instr, w_finished, w_f_lineno, w_f_locals, \ + w_f_trace, w_instr_lb, w_instr_ub, w_instr_prev_plus_one, w_stackdepth = args_w new_frame = self pycode = space.interp_w(PyCode, w_pycode) - if space.is_w(w_cells, space.w_None): - closure = None - cellvars = [] - else: - from pypy.interpreter.nestedscope import Cell - cells_w = space.unpackiterable(w_cells) - cells = [space.interp_w(Cell, w_cell) for w_cell in cells_w] - ncellvars = len(pycode.co_cellvars) - cellvars = cells[:ncellvars] - closure = cells[ncellvars:] + values_w = maker.slp_from_tuple_with_nulls(space, w_locals_cells_stack) + nfreevars = len(pycode.co_freevars) + closure = None + if nfreevars: + base = pycode.co_nlocals + len(pycode.co_cellvars) + closure = values_w[base: base + nfreevars] # do not use the instance's __init__ but the base's, because we set # everything like cells from here @@ -451,9 +501,12 @@ assert space.interp_w(Module, w_builtin) is space.builtin new_frame.set_blocklist([unpickle_block(space, w_blk) for w_blk in space.unpackiterable(w_blockstack)]) - values_w = maker.slp_from_tuple_with_nulls(space, w_valuestack) - for w_value in values_w: - new_frame.pushvalue(w_value) + self.locals_cells_stack_w = values_w[:] + valuestackdepth = space.int_w(w_stackdepth) + if not self._check_stack_index(valuestackdepth): + raise OperationError(space.w_ValueError, space.wrap("invalid stackdepth")) + assert valuestackdepth >= 0 + self.valuestackdepth = valuestackdepth if space.is_w(w_exc_value, space.w_None): new_frame.last_exception = None else: @@ -464,20 +517,17 @@ ) new_frame.last_instr = space.int_w(w_last_instr) new_frame.frame_finished_execution = space.is_true(w_finished) - new_frame.f_lineno = space.int_w(w_f_lineno) - fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals) - new_frame.locals_stack_w[:len(fastlocals_w)] = fastlocals_w + d = new_frame.getorcreatedebug() + d.f_lineno = space.int_w(w_f_lineno) if space.is_w(w_f_trace, space.w_None): - new_frame.w_f_trace = None + d.w_f_trace = None else: - new_frame.w_f_trace = w_f_trace + d.w_f_trace = w_f_trace - new_frame.instr_lb = space.int_w(w_instr_lb) #the three for tracing - new_frame.instr_ub = space.int_w(w_instr_ub) - new_frame.instr_prev_plus_one = space.int_w(w_instr_prev_plus_one) - - self._setcellvars(cellvars) + d.instr_lb = space.int_w(w_instr_lb) #the three for tracing + d.instr_ub = space.int_w(w_instr_ub) + d.instr_prev_plus_one = space.int_w(w_instr_prev_plus_one) def hide(self): return self.pycode.hidden_applevel @@ -492,10 +542,10 @@ scope_len = len(scope_w) if scope_len > self.pycode.co_nlocals: raise ValueError, "new fastscope is longer than the allocated area" - # don't assign directly to 'locals_stack_w[:scope_len]' to be + # don't assign directly to 'locals_cells_stack_w[:scope_len]' to be # virtualizable-friendly for i in range(scope_len): - self.locals_stack_w[i] = scope_w[i] + self.locals_cells_stack_w[i] = scope_w[i] self.init_cells() def getdictscope(self): @@ -503,30 +553,31 @@ Get the locals as a dictionary """ self.fast2locals() - return self.w_locals + return self.debugdata.w_locals def setdictscope(self, w_locals): """ Initialize the locals from a dictionary. """ - self.w_locals = w_locals + self.getorcreatedebug().w_locals = w_locals self.locals2fast() @jit.unroll_safe def fast2locals(self): # Copy values from the fastlocals to self.w_locals - if self.w_locals is None: - self.w_locals = self.space.newdict() + d = self.getorcreatedebug() + if d.w_locals is None: + d.w_locals = self.space.newdict() varnames = self.getcode().getvarnames() for i in range(min(len(varnames), self.getcode().co_nlocals)): name = varnames[i] - w_value = self.locals_stack_w[i] + w_value = self.locals_cells_stack_w[i] if w_value is not None: - self.space.setitem_str(self.w_locals, name, w_value) + self.space.setitem_str(d.w_locals, name, w_value) else: w_name = self.space.wrap(name.decode('utf-8')) try: - self.space.delitem(self.w_locals, w_name) + self.space.delitem(d.w_locals, w_name) except OperationError as e: if not e.match(self.space, self.space.w_KeyError): raise @@ -539,19 +590,20 @@ freevarnames = freevarnames + self.pycode.co_freevars for i in range(len(freevarnames)): name = freevarnames[i] - cell = self.cells[i] + cell = self._getcell(i) try: w_value = cell.get() except ValueError: pass else: - self.space.setitem_str(self.w_locals, name, w_value) + self.space.setitem_str(d.w_locals, name, w_value) @jit.unroll_safe def locals2fast(self): # Copy values from self.w_locals to the fastlocals - assert self.w_locals is not None + w_locals = self.getorcreatedebug().w_locals + assert w_locals is not None varnames = self.getcode().getvarnames() numlocals = self.getcode().co_nlocals @@ -559,7 +611,7 @@ for i in range(min(len(varnames), numlocals)): name = varnames[i] - w_value = self.space.finditem_str(self.w_locals, name) + w_value = self.space.finditem_str(w_locals, name) if w_value is not None: new_fastlocals_w[i] = w_value @@ -577,32 +629,29 @@ # into the locals dict used by the class. for i in range(len(freevarnames)): name = freevarnames[i] - cell = self.cells[i] - w_value = self.space.finditem_str(self.w_locals, name) + cell = self._getcell(i) + w_value = self.space.finditem_str(w_locals, name) if w_value is not None: cell.set(w_value) @jit.unroll_safe def init_cells(self): """ - Initialize cellvars from self.locals_stack_w. + Initialize cellvars from self.locals_cells_stack_w. """ args_to_copy = self.pycode._args_as_cellvars + index = self.pycode.co_nlocals for i in range(len(args_to_copy)): argnum = args_to_copy[i] if argnum >= 0: - self.cells[i].set(self.locals_stack_w[argnum]) + cell = self.locals_cells_stack_w[index] + assert isinstance(cell, Cell) + cell.set(self.locals_cells_stack_w[argnum]) + index += 1 def getclosure(self): return None - def _setcellvars(self, cellvars): - ncellvars = len(self.pycode.co_cellvars) - if len(cellvars) != ncellvars: - raise OperationError(self.space.w_TypeError, - self.space.wrap("bad cellvars")) - self.cells[:ncellvars] = cellvars - def fget_code(self, space): return space.wrap(self.getcode()) @@ -613,10 +662,10 @@ def fget_f_lineno(self, space): "Returns the line number of the instruction currently being executed." - if self.w_f_trace is None: + if self.get_w_f_trace() is None: return space.wrap(self.get_last_lineno()) else: - return space.wrap(self.f_lineno) + return space.wrap(self.getorcreatedebug().f_lineno) def fset_f_lineno(self, space, w_new_lineno): "Returns the line number of the instruction currently being executed." @@ -626,7 +675,7 @@ raise OperationError(space.w_ValueError, space.wrap("lineno must be an integer")) - if self.w_f_trace is None: + if self.get_w_f_trace() is None: raise OperationError(space.w_ValueError, space.wrap("f_lineno can only be set by a trace function.")) @@ -745,7 +794,7 @@ block.cleanup(self) f_iblock -= 1 - self.f_lineno = new_lineno + self.getorcreatedebug().f_lineno = new_lineno self.last_instr = new_lasti def get_last_lineno(self): @@ -763,17 +812,18 @@ return self.space.wrap(self.last_instr) def fget_f_trace(self, space): - return self.w_f_trace + return self.get_w_f_trace() def fset_f_trace(self, space, w_trace): if space.is_w(w_trace, space.w_None): - self.w_f_trace = None + self.getorcreatedebug().w_f_trace = None else: - self.w_f_trace = w_trace - self.f_lineno = self.get_last_lineno() + d = self.getorcreatedebug() + d.w_f_trace = w_trace + d.f_lineno = self.get_last_lineno() def fdel_f_trace(self, space): - self.w_f_trace = None + self.getorcreatedebug().w_f_trace = None def fget_f_exc_type(self, space): if self.last_exception is not None: diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -114,14 +114,14 @@ # dispatch_bytecode(), causing the real exception to be # raised after the exception handler block was popped. try: - trace = self.w_f_trace + trace = self.get_w_f_trace() if trace is not None: - self.w_f_trace = None + self.getorcreatedebug().w_f_trace = None try: ec.bytecode_trace_after_exception(self) finally: if trace is not None: - self.w_f_trace = trace + self.getorcreatedebug().w_f_trace = trace except OperationError, e: operr = e pytraceback.record_application_traceback( @@ -455,7 +455,7 @@ def LOAD_FAST(self, varindex, next_instr): # access a local variable directly - w_value = self.locals_stack_w[varindex] + w_value = self.locals_cells_stack_w[varindex] if w_value is None: self._load_fast_failed(varindex) self.pushvalue(w_value) @@ -475,7 +475,7 @@ def STORE_FAST(self, varindex, next_instr): w_newvalue = self.popvalue() assert w_newvalue is not None - self.locals_stack_w[varindex] = w_newvalue + self.locals_cells_stack_w[varindex] = w_newvalue def getfreevarname(self, index): freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars @@ -487,7 +487,7 @@ def LOAD_DEREF(self, varindex, next_instr): # nested scopes: access a variable through its cell object - cell = self.cells[varindex] + cell = self._getcell(varindex) try: w_value = cell.get() except ValueError: @@ -498,11 +498,11 @@ def STORE_DEREF(self, varindex, next_instr): # nested scopes: access a variable through its cell object w_newvalue = self.popvalue() - cell = self.cells[varindex] + cell = self._getcell(varindex) cell.set(w_newvalue) def DELETE_DEREF(self, varindex, next_instr): - cell = self.cells[varindex] + cell = self._getcell(varindex) try: cell.get() except ValueError: @@ -523,7 +523,7 @@ def LOAD_CLOSURE(self, varindex, next_instr): # nested scopes: access the cell object - cell = self.cells[varindex] + cell = self._getcell(varindex) w_value = self.space.wrap(cell) self.pushvalue(w_value) @@ -684,10 +684,10 @@ raise operror def LOAD_LOCALS(self, oparg, next_instr): - self.pushvalue(self.w_locals) + self.pushvalue(self.getorcreatedebug().w_locals) def STORE_LOCALS(self, oparg, next_instr): - self.w_locals = self.popvalue() + self.getorcreatedebug().w_locals = self.popvalue() def exec_(self, w_prog, w_globals, w_locals): """The builtins.exec function.""" @@ -709,8 +709,8 @@ space.call_method(w_globals, 'setdefault', space.wrap('__builtins__'), space.wrap(self.get_builtin())) - plain = (self.w_locals is not None and - space.is_w(w_locals, self.w_locals)) + plain = (self.get_w_locals() is not None and + space.is_w(w_locals, self.get_w_locals())) if plain: w_locals = self.getdictscope() code.exec_code(space, w_globals, w_locals) @@ -761,12 +761,13 @@ def STORE_NAME(self, varindex, next_instr): varname = self.getname_u(varindex) w_newvalue = self.popvalue() - self.space.setitem_str(self.w_locals, varname, w_newvalue) + self.space.setitem_str(self.getorcreatedebug().w_locals, varname, + w_newvalue) def DELETE_NAME(self, varindex, next_instr): w_varname = self.getname_w(varindex) try: - self.space.delitem(self.w_locals, w_varname) + self.space.delitem(self.getorcreatedebug().w_locals, w_varname) except OperationError, e: # catch KeyErrors and turn them into NameErrors if not e.match(self.space, self.space.w_KeyError): @@ -834,8 +835,9 @@ def LOAD_NAME(self, nameindex, next_instr): w_varname = self.getname_w(nameindex) varname = self.space.identifier_w(w_varname) - if self.w_locals is not self.w_globals: - w_value = self.space.finditem_str(self.w_locals, varname) + if self.getorcreatedebug().w_locals is not self.w_globals: + w_value = self.space.finditem_str(self.getorcreatedebug().w_locals, + varname) if w_value is not None: self.pushvalue(w_value) return @@ -868,12 +870,12 @@ LOAD_GLOBAL._always_inline_ = True def DELETE_FAST(self, varindex, next_instr): - if self.locals_stack_w[varindex] is None: + if self.locals_cells_stack_w[varindex] is None: varname = self.getlocalvarname(varindex) raise oefmt(self.space.w_UnboundLocalError, "local variable '%s' referenced before assignment", varname) - self.locals_stack_w[varindex] = None + self.locals_cells_stack_w[varindex] = None def BUILD_TUPLE(self, itemcount, next_instr): items = self.popvalues(itemcount) @@ -971,7 +973,11 @@ if w_import is None: raise OperationError(space.w_ImportError, space.wrap("__import__ not found")) - w_locals = self.w_locals + d = self.getdebug() + if d is None: + w_locals = None + else: + w_locals = d.w_locals if w_locals is None: # CPython does this w_locals = space.w_None w_globals = self.w_globals @@ -1148,7 +1154,7 @@ args = self.argument_factory(arguments, keywords, keywords_w, w_star, w_starstar) w_function = self.popvalue() - if self.is_being_profiled and function.is_builtin_code(w_function): + if self.get_is_being_profiled() and function.is_builtin_code(w_function): w_result = self.space.call_args_and_c_profile(self, w_function, args) else: diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -181,6 +181,11 @@ self.check([], {'PYTHONNOUSERSITE': '1'}, sys_argv=[''], run_stdin=True, no_user_site=1) self.check([], {'PYTHONUNBUFFERED': '1'}, sys_argv=[''], run_stdin=True, unbuffered=1) self.check([], {'PYTHONVERBOSE': '1'}, sys_argv=[''], run_stdin=True, verbose=1) + self.check([], {'PYTHONOPTIMIZE': '1'}, sys_argv=[''], run_stdin=True, optimize=1) + self.check([], {'PYTHONOPTIMIZE': '0'}, sys_argv=[''], run_stdin=True, optimize=1) + self.check([], {'PYTHONOPTIMIZE': '10'}, sys_argv=[''], run_stdin=True, optimize=10) + self.check(['-O'], {'PYTHONOPTIMIZE': '10'}, sys_argv=[''], run_stdin=True, optimize=10) + self.check(['-OOO'], {'PYTHONOPTIMIZE': 'abc'}, sys_argv=[''], run_stdin=True, optimize=3) def test_sysflags(self): flags = ( diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -548,7 +548,7 @@ __objclass__ = GetSetProperty(GetSetProperty.descr_get_objclass), __doc__ = interp_attrproperty('doc', cls=GetSetProperty), ) -GetSetProperty.typedef.acceptable_as_base_class = False +assert not GetSetProperty.typedef.acceptable_as_base_class # no __new__ class Member(W_Root): @@ -602,7 +602,7 @@ __name__ = interp_attrproperty('name', cls=Member), __objclass__ = interp_attrproperty_w('w_cls', cls=Member), ) -Member.typedef.acceptable_as_base_class = False +assert not Member.typedef.acceptable_as_base_class # no __new__ # ____________________________________________________________ @@ -722,7 +722,7 @@ co_flags = GetSetProperty(fget_co_flags, cls=Code), co_consts = GetSetProperty(fget_co_consts, cls=Code), ) -Code.typedef.acceptable_as_base_class = False +assert not Code.typedef.acceptable_as_base_class # no __new__ BuiltinCode.typedef = TypeDef('builtin-code', __reduce__ = interp2app(BuiltinCode.descr__reduce__), @@ -733,7 +733,7 @@ co_flags = GetSetProperty(fget_co_flags, cls=BuiltinCode), co_consts = GetSetProperty(fget_co_consts, cls=BuiltinCode), ) -BuiltinCode.typedef.acceptable_as_base_class = False +assert not BuiltinCode.typedef.acceptable_as_base_class # no __new__ PyCode.typedef = TypeDef('code', @@ -779,7 +779,7 @@ f_locals = GetSetProperty(PyFrame.fget_getdictscope), f_globals = interp_attrproperty_w('w_globals', cls=PyFrame), ) -PyFrame.typedef.acceptable_as_base_class = False +assert not PyFrame.typedef.acceptable_as_base_class # no __new__ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit