[pypy-commit] pypy dynamic-specialized-tuple: Merged default in.
alex_gaynor
noreply at buildbot.pypy.org
Mon Apr 23 16:00:54 CEST 2012
Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: dynamic-specialized-tuple
Changeset: r54678:1cdf2585f691
Date: 2012-04-23 08:29 -0400
http://bitbucket.org/pypy/pypy/changeset/1cdf2585f691/
Log: Merged default in.
diff --git a/lib_pypy/_ctypes/builtin.py b/lib_pypy/_ctypes/builtin.py
--- a/lib_pypy/_ctypes/builtin.py
+++ b/lib_pypy/_ctypes/builtin.py
@@ -3,7 +3,8 @@
try:
from thread import _local as local
except ImportError:
- local = object # no threads
+ class local(object): # no threads
+ pass
class ConvMode:
encoding = 'ascii'
diff --git a/lib_pypy/_ctypes_test.py b/lib_pypy/_ctypes_test.py
--- a/lib_pypy/_ctypes_test.py
+++ b/lib_pypy/_ctypes_test.py
@@ -21,7 +21,7 @@
# Compile .c file
include_dir = os.path.join(thisdir, '..', 'include')
if sys.platform == 'win32':
- ccflags = []
+ ccflags = ['-D_CRT_SECURE_NO_WARNINGS']
else:
ccflags = ['-fPIC']
res = compiler.compile([os.path.join(thisdir, '_ctypes_test.c')],
@@ -34,6 +34,13 @@
if sys.platform == 'win32':
# XXX libpypy-c.lib is currently not installed automatically
library = os.path.join(thisdir, '..', 'include', 'libpypy-c')
+ if not os.path.exists(library + '.lib'):
+ #For a nightly build
+ library = os.path.join(thisdir, '..', 'include', 'python27')
+ if not os.path.exists(library + '.lib'):
+ # For a local translation
+ library = os.path.join(thisdir, '..', 'pypy', 'translator',
+ 'goal', 'libpypy-c')
libraries = [library, 'oleaut32']
extra_ldargs = ['/MANIFEST'] # needed for VC10
else:
diff --git a/lib_pypy/_testcapi.py b/lib_pypy/_testcapi.py
--- a/lib_pypy/_testcapi.py
+++ b/lib_pypy/_testcapi.py
@@ -16,7 +16,7 @@
# Compile .c file
include_dir = os.path.join(thisdir, '..', 'include')
if sys.platform == 'win32':
- ccflags = []
+ ccflags = ['-D_CRT_SECURE_NO_WARNINGS']
else:
ccflags = ['-fPIC', '-Wimplicit-function-declaration']
res = compiler.compile([os.path.join(thisdir, '_testcapimodule.c')],
@@ -29,6 +29,13 @@
if sys.platform == 'win32':
# XXX libpypy-c.lib is currently not installed automatically
library = os.path.join(thisdir, '..', 'include', 'libpypy-c')
+ if not os.path.exists(library + '.lib'):
+ #For a nightly build
+ library = os.path.join(thisdir, '..', 'include', 'python27')
+ if not os.path.exists(library + '.lib'):
+ # For a local translation
+ library = os.path.join(thisdir, '..', 'pypy', 'translator',
+ 'goal', 'libpypy-c')
libraries = [library, 'oleaut32']
extra_ldargs = ['/MANIFEST', # needed for VC10
'/EXPORT:init_testcapi']
diff --git a/lib_pypy/pyrepl/reader.py b/lib_pypy/pyrepl/reader.py
--- a/lib_pypy/pyrepl/reader.py
+++ b/lib_pypy/pyrepl/reader.py
@@ -152,8 +152,8 @@
(r'\<delete>', 'delete'),
(r'\<backspace>', 'backspace'),
(r'\M-\<backspace>', 'backward-kill-word'),
- (r'\<end>', 'end'),
- (r'\<home>', 'home'),
+ (r'\<end>', 'end-of-line'), # was 'end'
+ (r'\<home>', 'beginning-of-line'), # was 'home'
(r'\<f1>', 'help'),
(r'\EOF', 'end'), # the entries in the terminfo database for xterms
(r'\EOH', 'home'), # seem to be wrong. this is a less than ideal
diff --git a/pypy/annotation/description.py b/pypy/annotation/description.py
--- a/pypy/annotation/description.py
+++ b/pypy/annotation/description.py
@@ -229,8 +229,8 @@
return thing
elif hasattr(thing, '__name__'): # mostly types and functions
return thing.__name__
- elif hasattr(thing, 'name'): # mostly ClassDescs
- return thing.name
+ elif hasattr(thing, 'name') and isinstance(thing.name, str):
+ return thing.name # mostly ClassDescs
elif isinstance(thing, tuple):
return '_'.join(map(nameof, thing))
else:
diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/cppyy.rst
@@ -0,0 +1,333 @@
+============================
+cppyy: C++ bindings for PyPy
+============================
+
+The cppyy module provides C++ bindings for PyPy by using the reflection
+information extracted from C++ header files by means of the
+`Reflex package`_.
+For this to work, you have to both install Reflex and build PyPy from the
+reflex-support branch.
+As indicated by this being a branch, support for Reflex is still
+experimental.
+However, it is functional enough to put it in the hands of those who want
+to give it a try.
+In the medium term, cppyy will move away from Reflex and instead use
+`cling`_ as its backend, which is based on `llvm`_.
+Although that will change the logistics on the generation of reflection
+information, it will not change the python-side interface.
+
+.. _`Reflex package`: http://root.cern.ch/drupal/content/reflex
+.. _`cling`: http://root.cern.ch/drupal/content/cling
+.. _`llvm`: http://llvm.org/
+
+
+Installation
+============
+
+For now, the easiest way of getting the latest version of Reflex, is by
+installing the ROOT package.
+Besides getting the latest version of Reflex, another advantage is that with
+the full ROOT package, you can also use your Reflex-bound code on `CPython`_.
+`Download`_ a binary or install from `source`_.
+Some Linux and Mac systems may have ROOT provided in the list of scientific
+software of their packager.
+A current, standalone version of Reflex should be provided at some point,
+once the dependencies and general packaging have been thought out.
+Also, make sure you have a version of `gccxml`_ installed, which is most
+easily provided by the packager of your system.
+If you read up on gccxml, you'll probably notice that it is no longer being
+developed and hence will not provide C++11 support.
+That's why the medium term plan is to move to `cling`_.
+
+.. _`Download`: http://root.cern.ch/drupal/content/downloading-root
+.. _`source`: http://root.cern.ch/drupal/content/installing-root-source
+.. _`gccxml`: http://www.gccxml.org
+
+Next, get the `PyPy sources`_, select the reflex-support branch, and build
+pypy-c.
+For the build to succeed, the ``$ROOTSYS`` environment variable must point to
+the location of your ROOT installation::
+
+ $ hg clone https://bitbucket.org/pypy/pypy
+ $ cd pypy
+ $ hg up reflex-support
+ $ cd pypy/translator/goal
+ $ python translate.py -O jit --gcrootfinder=shadowstack targetpypystandalone.py --withmod-cppyy
+
+This will build a ``pypy-c`` that includes the cppyy module, and through that,
+Reflex support.
+Of course, if you already have a pre-built version of the ``pypy`` interpreter,
+you can use that for the translation rather than ``python``.
+
+.. _`PyPy sources`: https://bitbucket.org/pypy/pypy/overview
+
+
+Basic example
+=============
+
+Now test with a trivial example whether all packages are properly installed
+and functional.
+First, create a C++ header file with some class in it (note that all functions
+are made inline for convenience; a real-world example would of course have a
+corresponding source file)::
+
+ $ cat MyClass.h
+ class MyClass {
+ public:
+ MyClass(int i = -99) : m_myint(i) {}
+
+ int GetMyInt() { return m_myint; }
+ void SetMyInt(int i) { m_myint = i; }
+
+ public:
+ int m_myint;
+ };
+
+Then, generate the bindings using ``genreflex`` (part of ROOT), and compile the
+code::
+
+ $ genreflex MyClass.h
+ $ g++ -fPIC -rdynamic -O2 -shared -I$ROOTSYS/include MyClass_rflx.cpp -o libMyClassDict.so
+
+Now you're ready to use the bindings.
+Since the bindings are designed to look pythonistic, it should be
+straightforward::
+
+ $ pypy-c
+ >>>> import cppyy
+ >>>> cppyy.load_reflection_info("libMyClassDict.so")
+ <CPPLibrary object at 0xb6fd7c4c>
+ >>>> myinst = cppyy.gbl.MyClass(42)
+ >>>> print myinst.GetMyInt()
+ 42
+ >>>> myinst.SetMyInt(33)
+ >>>> print myinst.m_myint
+ 33
+ >>>> myinst.m_myint = 77
+ >>>> print myinst.GetMyInt()
+ 77
+ >>>> help(cppyy.gbl.MyClass) # shows that normal python introspection works
+
+That's all there is to it!
+
+
+Features
+========
+
+The following is not meant to be an exhaustive list, since cppyy is still
+under active development.
+Furthermore, the intention is that every feature is as natural as possible on
+the python side, so if you find something missing in the list below, simply
+try it out.
+It is not always possible to provide exact mapping between python and C++
+(active memory management is one such case), but by and large, if the use of a
+feature does not strike you as obvious, it is more likely to simply be a bug.
+That is a strong statement to make, but also a worthy goal.
+
+* **abstract classes**: Are represented as python classes, since they are
+ needed to complete the inheritance hierarchies, but will raise an exception
+ if an attempt is made to instantiate from them.
+
+* **arrays**: Supported for builtin data types only, as used from module
+ ``array``.
+ Out-of-bounds checking is limited to those cases where the size is known at
+ compile time (and hence part of the reflection info).
+
+* **builtin data types**: Map onto the expected equivalent python types, with
+ the caveat that there may be size differences, and thus it is possible that
+ exceptions are raised if an overflow is detected.
+
+* **casting**: Is supposed to be unnecessary.
+ Object pointer returns from functions provide the most derived class known
+ in the hierarchy of the object being returned.
+ This is important to preserve object identity as well as to make casting,
+ a pure C++ feature after all, superfluous.
+
+* **classes and structs**: Get mapped onto python classes, where they can be
+ instantiated as expected.
+ If classes are inner classes or live in a namespace, their naming and
+ location will reflect that.
+
+* **data members**: Public data members are represented as python properties
+ and provide read and write access on instances as expected.
+
+* **default arguments**: C++ default arguments work as expected, but python
+ keywords are not supported.
+ It is technically possible to support keywords, but for the C++ interface,
+ the formal argument names have no meaning and are not considered part of the
+ API, hence it is not a good idea to use keywords.
+
+* **doc strings**: The doc string of a method or function contains the C++
+ arguments and return types of all overloads of that name, as applicable.
+
+* **functions**: Work as expected and live in their appropriate namespace
+ (which can be the global one, ``cppyy.gbl``).
+
+* **inheritance**: All combinations of inheritance on the C++ (single,
+ multiple, virtual) are supported in the binding.
+ However, new python classes can only use single inheritance from a bound C++
+ class.
+ Multiple inheritance would introduce two "this" pointers in the binding.
+ This is a current, not a fundamental, limitation.
+ The C++ side will not see any overridden methods on the python side, as
+ cross-inheritance is planned but not yet supported.
+
+* **methods**: Are represented as python methods and work as expected.
+ They are first class objects and can be bound to an instance.
+ Virtual C++ methods work as expected.
+ To select a specific virtual method, do like with normal python classes
+ that override methods: select it from the class that you need, rather than
+ calling the method on the instance.
+
+* **namespaces**: Are represented as python classes.
+ Namespaces are more open-ended than classes, so sometimes initial access may
+ result in updates as data and functions are looked up and constructed
+ lazily.
+ Thus the result of ``dir()`` on a namespace should not be relied upon: it
+ only shows the already accessed members. (TODO: to be fixed by implementing
+ __dir__.)
+ The global namespace is ``cppyy.gbl``.
+
+* **operator conversions**: If defined in the C++ class and a python
+ equivalent exists (i.e. all builtin integer and floating point types, as well
+ as ``bool``), it will map onto that python conversion.
+ Note that ``char*`` is mapped onto ``__str__``.
+
+* **operator overloads**: If defined in the C++ class and if a python
+ equivalent is available (not always the case, think e.g. of ``operator||``),
+ then they work as expected.
+ Special care needs to be taken for global operator overloads in C++: first,
+ make sure that they are actually reflected, especially for the global
+ overloads for ``operator==`` and ``operator!=`` of STL iterators in the case
+ of gcc.
+ Second, make sure that reflection info is loaded in the proper order.
+ I.e. that these global overloads are available before use.
+
+* **pointers**: For builtin data types, see arrays.
+ For objects, a pointer to an object and an object looks the same, unless
+ the pointer is a data member.
+ In that case, assigning to the data member will cause a copy of the pointer
+ and care should be taken about the object's life time.
+ If a pointer is a global variable, the C++ side can replace the underlying
+ object and the python side will immediately reflect that.
+
+* **static data members**: Are represented as python property objects on the
+ class and the meta-class.
+ Both read and write access is as expected.
+
+* **static methods**: Are represented as python's ``staticmethod`` objects
+ and can be called both from the class as well as from instances.
+
+* **strings**: The std::string class is considered a builtin C++ type and
+ mixes quite well with python's str.
+ Python's str can be passed where a ``const char*`` is expected, and an str
+ will be returned if the return type is ``const char*``.
+
+* **templated classes**: Are represented in a meta-class style in python.
+ This looks a little bit confusing, but conceptually is rather natural.
+ For example, given the class ``std::vector<int>``, the meta-class part would
+ be ``std.vector`` in python.
+ Then, to get the instantiation on ``int``, do ``std.vector(int)`` and to
+ create an instance of that class, do ``std.vector(int)()``.
+ Note that templates can be build up by handing actual types to the class
+ instantiation (as done in this vector example), or by passing in the list of
+ template arguments as a string.
+ The former is a lot easier to work with if you have template instantiations
+ using classes that themselves are templates (etc.) in the arguments.
+ All classes must already exist in the loaded reflection info.
+
+* **unary operators**: Are supported if a python equivalent exists, and if the
+ operator is defined in the C++ class.
+
+You can always find more detailed examples and see the full of supported
+features by looking at the tests in pypy/module/cppyy/test.
+
+If a feature or reflection info is missing, this is supposed to be handled
+gracefully.
+In fact, there are unit tests explicitly for this purpose (even as their use
+becomes less interesting over time, as the number of missing features
+decreases).
+Only when a missing feature is used, should there be an exception.
+For example, if no reflection info is available for a return type, then a
+class that has a method with that return type can still be used.
+Only that one specific method can not be used.
+
+
+The fast lane
+=============
+
+The following is an experimental feature of cppyy, and that makes it doubly
+experimental, so caveat emptor.
+With a slight modification of Reflex, it can provide function pointers for
+C++ methods, and hence allow PyPy to call those pointers directly, rather than
+calling C++ through a Reflex stub.
+This results in a rather significant speed-up.
+Mind you, the normal stub path is not exactly slow, so for now only use this
+out of curiosity or if you really need it.
+
+To install this patch of Reflex, locate the file genreflex-methptrgetter.patch
+in pypy/module/cppyy and apply it to the genreflex python scripts found in
+``$ROOTSYS/lib``::
+
+ $ cd $ROOTSYS/lib
+ $ patch -p2 < genreflex-methptrgetter.patch
+
+With this patch, ``genreflex`` will have grown the ``--with-methptrgetter``
+option.
+Use this option when running ``genreflex``, and add the
+``-Wno-pmf-conversions`` option to ``g++`` when compiling.
+The rest works the same way: the fast path will be used transparently (which
+also means that you can't actually find out whether it is in use, other than
+by running a micro-benchmark).
+
+
+CPython
+=======
+
+Most of the ideas in cppyy come originally from the `PyROOT`_ project.
+Although PyROOT does not support Reflex directly, it has an alter ego called
+"PyCintex" that, in a somewhat roundabout way, does.
+If you installed ROOT, rather than just Reflex, PyCintex should be available
+immediately if you add ``$ROOTSYS/lib`` to the ``PYTHONPATH`` environment
+variable.
+
+.. _`PyROOT`: http://root.cern.ch/drupal/content/pyroot
+
+There are a couple of minor differences between PyCintex and cppyy, most to do
+with naming.
+The one that you will run into directly, is that PyCintex uses a function
+called ``loadDictionary`` rather than ``load_reflection_info``.
+The reason for this is that Reflex calls the shared libraries that contain
+reflection info "dictionaries."
+However, in python, the name `dictionary` already has a well-defined meaning,
+so a more descriptive name was chosen for cppyy.
+In addition, PyCintex requires that the names of shared libraries so loaded
+start with "lib" in their name.
+The basic example above, rewritten for PyCintex thus goes like this::
+
+ $ python
+ >>> import PyCintex
+ >>> PyCintex.loadDictionary("libMyClassDict.so")
+ >>> myinst = PyCintex.gbl.MyClass(42)
+ >>> print myinst.GetMyInt()
+ 42
+ >>> myinst.SetMyInt(33)
+ >>> print myinst.m_myint
+ 33
+ >>> myinst.m_myint = 77
+ >>> print myinst.GetMyInt()
+ 77
+ >>> help(PyCintex.gbl.MyClass) # shows that normal python introspection works
+
+Other naming differences are such things as taking an address of an object.
+In PyCintex, this is done with ``AddressOf`` whereas in cppyy the choice was
+made to follow the naming as in ``ctypes`` and hence use ``addressof``
+(PyROOT/PyCintex predate ``ctypes`` by several years, and the ROOT project
+follows camel-case, hence the differences).
+
+Of course, this is python, so if any of the naming is not to your liking, all
+you have to do is provide a wrapper script that you import instead of
+importing the ``cppyy`` or ``PyCintex`` modules directly.
+In that wrapper script you can rename methods exactly the way you need it.
+
+In the cling world, all these differences will be resolved.
diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst
--- a/pypy/doc/extending.rst
+++ b/pypy/doc/extending.rst
@@ -23,6 +23,8 @@
* Write them in RPython as mixedmodule_, using *rffi* as bindings.
+* Write them in C++ and bind them through Reflex_ (EXPERIMENTAL)
+
.. _ctypes: #CTypes
.. _\_ffi: #LibFFI
.. _mixedmodule: #Mixed Modules
@@ -110,3 +112,34 @@
XXX we should provide detailed docs about lltype and rffi, especially if we
want people to follow that way.
+
+Reflex
+======
+
+This method is only experimental for now, and is being exercised on a branch,
+`reflex-support`_, so you will have to build PyPy yourself.
+The method works by using the `Reflex package`_ to provide reflection
+information of the C++ code, which is then used to automatically generate
+bindings at runtime, which can then be used from python.
+Full details are `available here`_.
+
+.. _`reflex-support`: cppyy.html
+.. _`Reflex package`: http://root.cern.ch/drupal/content/reflex
+.. _`available here`: cppyy.html
+
+Pros
+----
+
+If it works, it is mostly automatic, and hence easy in use.
+The bindings can make use of direct pointers, in which case the calls are
+very fast.
+
+Cons
+----
+
+C++ is a large language, and these bindings are not yet feature-complete.
+Although missing features should do no harm if you don't use them, if you do
+need a particular feature, it may be necessary to work around it in python
+or with a C++ helper function.
+Although Reflex works on various platforms, the bindings with PyPy have only
+been tested on Linux.
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -49,7 +49,9 @@
def __repr__(self):
# return "function %s.%s" % (self.space, self.name)
# maybe we want this shorter:
- name = getattr(self, 'name', '?')
+ name = getattr(self, 'name', None)
+ if not isinstance(name, str):
+ name = '?'
return "<%s %s>" % (self.__class__.__name__, name)
def call_args(self, args):
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -1656,15 +1656,21 @@
else:
# XXX hard-coded assumption: to go from an object to its class
# we use the following algorithm:
- # - read the typeid from mem(locs[0]), i.e. at offset 0
- # - keep the lower 16 bits read there
- # - multiply by 4 and use it as an offset in type_info_group
- # - add 16 bytes, to go past the TYPE_INFO structure
+ # - read the typeid from mem(locs[0]), i.e. at offset 0;
+ # this is a complete word (N=4 bytes on 32-bit, N=8 on
+ # 64-bits)
+ # - keep the lower half of what is read there (i.e.
+ # truncate to an unsigned 'N / 2' bytes value)
+ # - multiply by 4 (on 32-bits only) and use it as an
+ # offset in type_info_group
+ # - add 16/32 bytes, to go past the TYPE_INFO structure
loc = locs[1]
assert isinstance(loc, ImmedLoc)
classptr = loc.value
# here, we have to go back from 'classptr' to the value expected
- # from reading the 16 bits in the object header
+ # from reading the half-word in the object header. Note that
+ # this half-word is at offset 0 on a little-endian machine;
+ # it would be at offset 2 or 4 on a big-endian machine.
from pypy.rpython.memory.gctypelayout import GCData
sizeof_ti = rffi.sizeof(GCData.TYPE_INFO)
type_info_group = llop.gc_get_type_info_group(llmemory.Address)
diff --git a/pypy/module/cpyext/iterator.py b/pypy/module/cpyext/iterator.py
--- a/pypy/module/cpyext/iterator.py
+++ b/pypy/module/cpyext/iterator.py
@@ -22,7 +22,7 @@
cannot be iterated."""
return space.iter(w_obj)
- at cpython_api([PyObject], PyObject, error=CANNOT_FAIL)
+ at cpython_api([PyObject], PyObject)
def PyIter_Next(space, w_obj):
"""Return the next value from the iteration o. If the object is an
iterator, this retrieves the next value from the iteration, and returns
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -110,6 +110,16 @@
space.call_method(w_list, "reverse")
return 0
+ at cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
+def PyList_GetSlice(space, w_list, low, high):
+ """Return a list of the objects in list containing the objects between low
+ and high. Return NULL and set an exception if unsuccessful. Analogous
+ to list[low:high]. Negative indices, as when slicing from Python, are not
+ supported."""
+ w_start = space.wrap(low)
+ w_stop = space.wrap(high)
+ return space.getslice(w_list, w_start, w_stop)
+
@cpython_api([PyObject, Py_ssize_t, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
def PyList_SetSlice(space, w_list, low, high, w_sequence):
"""Set the slice of list between low and high to the contents of
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -381,6 +381,15 @@
This is the equivalent of the Python expression hash(o)."""
return space.int_w(space.hash(w_obj))
+ at cpython_api([PyObject], lltype.Signed, error=-1)
+def PyObject_HashNotImplemented(space, o):
+ """Set a TypeError indicating that type(o) is not hashable and return -1.
+ This function receives special treatment when stored in a tp_hash slot,
+ allowing a type to explicitly indicate to the interpreter that it is not
+ hashable.
+ """
+ raise OperationError(space.w_TypeError, space.wrap("unhashable type"))
+
@cpython_api([PyObject], PyObject)
def PyObject_Dir(space, w_o):
"""This is equivalent to the Python expression dir(o), returning a (possibly
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -314,7 +314,10 @@
"""This function simulates the effect of a SIGINT signal arriving --- the
next time PyErr_CheckSignals() is called, KeyboardInterrupt will be raised.
It may be called without holding the interpreter lock."""
- space.check_signal_action.set_interrupt()
+ if space.check_signal_action is not None:
+ space.check_signal_action.set_interrupt()
+ #else:
+ # no 'signal' module present, ignore... We can't return an error here
@cpython_api([PyObjectP, PyObjectP, PyObjectP], lltype.Void)
def PyErr_GetExcInfo(space, ptype, pvalue, ptraceback):
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -7,7 +7,7 @@
cpython_api, generic_cpy_call, PyObject, Py_ssize_t)
from pypy.module.cpyext.typeobjectdefs import (
unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc,
- getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc,
+ getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
readbufferproc)
@@ -60,6 +60,16 @@
args_w = space.fixedview(w_args)
return generic_cpy_call(space, func_binary, w_self, args_w[0])
+def wrap_inquirypred(space, w_self, w_args, func):
+ func_inquiry = rffi.cast(inquiry, func)
+ check_num_args(space, w_args, 0)
+ args_w = space.fixedview(w_args)
+ res = generic_cpy_call(space, func_inquiry, w_self)
+ res = rffi.cast(lltype.Signed, res)
+ if res == -1:
+ space.fromcache(State).check_and_raise_exception()
+ return space.wrap(bool(res))
+
def wrap_getattr(space, w_self, w_args, func):
func_target = rffi.cast(getattrfunc, func)
check_num_args(space, w_args, 1)
diff --git a/pypy/module/cpyext/stringobject.py b/pypy/module/cpyext/stringobject.py
--- a/pypy/module/cpyext/stringobject.py
+++ b/pypy/module/cpyext/stringobject.py
@@ -294,6 +294,26 @@
w_errors = space.wrap(rffi.charp2str(errors))
return space.call_method(w_str, 'encode', w_encoding, w_errors)
+ at cpython_api([PyObject, rffi.CCHARP, rffi.CCHARP], PyObject)
+def PyString_AsDecodedObject(space, w_str, encoding, errors):
+ """Decode a string object by passing it to the codec registered
+ for encoding and return the result as Python object. encoding and
+ errors have the same meaning as the parameters of the same name in
+ the string encode() method. The codec to be used is looked up
+ using the Python codec registry. Return NULL if an exception was
+ raised by the codec.
+
+ This function is not available in 3.x and does not have a PyBytes alias."""
+ if not PyString_Check(space, w_str):
+ PyErr_BadArgument(space)
+
+ w_encoding = w_errors = space.w_None
+ if encoding:
+ w_encoding = space.wrap(rffi.charp2str(encoding))
+ if errors:
+ w_errors = space.wrap(rffi.charp2str(errors))
+ return space.call_method(w_str, "decode", w_encoding, w_errors)
+
@cpython_api([PyObject, PyObject], PyObject)
def _PyString_Join(space, w_sep, w_seq):
return space.call_method(w_sep, 'join', w_seq)
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -1405,17 +1405,6 @@
"""
raise NotImplementedError
- at cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
-def PyList_GetSlice(space, list, low, high):
- """Return a list of the objects in list containing the objects between low
- and high. Return NULL and set an exception if unsuccessful. Analogous
- to list[low:high]. Negative indices, as when slicing from Python, are not
- supported.
-
- This function used an int for low and high. This might
- require changes in your code for properly supporting 64-bit systems."""
- raise NotImplementedError
-
@cpython_api([Py_ssize_t], PyObject)
def PyLong_FromSsize_t(space, v):
"""Return a new PyLongObject object from a C Py_ssize_t, or
@@ -1606,15 +1595,6 @@
for PyObject_Str()."""
raise NotImplementedError
- at cpython_api([PyObject], lltype.Signed, error=-1)
-def PyObject_HashNotImplemented(space, o):
- """Set a TypeError indicating that type(o) is not hashable and return -1.
- This function receives special treatment when stored in a tp_hash slot,
- allowing a type to explicitly indicate to the interpreter that it is not
- hashable.
- """
- raise NotImplementedError
-
@cpython_api([], PyFrameObject)
def PyEval_GetFrame(space):
"""Return the current thread state's frame, which is NULL if no frame is
@@ -1737,17 +1717,6 @@
changes in your code for properly supporting 64-bit systems."""
raise NotImplementedError
- at cpython_api([PyObject, rffi.CCHARP, rffi.CCHARP], PyObject)
-def PyString_AsDecodedObject(space, str, encoding, errors):
- """Decode a string object by passing it to the codec registered for encoding and
- return the result as Python object. encoding and errors have the same
- meaning as the parameters of the same name in the string encode() method.
- The codec to be used is looked up using the Python codec registry. Return NULL
- if an exception was raised by the codec.
-
- This function is not available in 3.x and does not have a PyBytes alias."""
- raise NotImplementedError
-
@cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP, rffi.CCHARP], PyObject)
def PyString_Encode(space, s, size, encoding, errors):
"""Encode the char buffer of the given size by passing it to the codec
diff --git a/pypy/module/cpyext/test/test_iterator.py b/pypy/module/cpyext/test/test_iterator.py
--- a/pypy/module/cpyext/test/test_iterator.py
+++ b/pypy/module/cpyext/test/test_iterator.py
@@ -15,3 +15,8 @@
assert space.unwrap(api.PyIter_Next(w_iter)) == 3
assert api.PyIter_Next(w_iter) is None
assert not api.PyErr_Occurred()
+
+ def test_iternext_error(self,space, api):
+ assert api.PyIter_Next(space.w_None) is None
+ assert api.PyErr_Occurred() is space.w_TypeError
+ api.PyErr_Clear()
diff --git a/pypy/module/cpyext/test/test_listobject.py b/pypy/module/cpyext/test/test_listobject.py
--- a/pypy/module/cpyext/test/test_listobject.py
+++ b/pypy/module/cpyext/test/test_listobject.py
@@ -58,6 +58,11 @@
w_t = api.PyList_AsTuple(w_l)
assert space.unwrap(w_t) == (3, 2, 1)
+ def test_list_getslice(self, space, api):
+ w_l = space.newlist([space.wrap(3), space.wrap(2), space.wrap(1)])
+ w_s = api.PyList_GetSlice(w_l, 1, 5)
+ assert space.unwrap(w_s) == [2, 1]
+
class AppTestListObject(AppTestCpythonExtensionBase):
def test_listobject(self):
import sys
diff --git a/pypy/module/cpyext/test/test_stringobject.py b/pypy/module/cpyext/test/test_stringobject.py
--- a/pypy/module/cpyext/test/test_stringobject.py
+++ b/pypy/module/cpyext/test/test_stringobject.py
@@ -307,6 +307,13 @@
space.wrap(2), lltype.nullptr(rffi.CCHARP.TO), lltype.nullptr(rffi.CCHARP.TO)
)
+ def test_AsDecodedObject(self, space, api):
+ w_str = space.wrap('caf\xe9')
+ encoding = rffi.str2charp("latin-1")
+ w_res = api.PyString_AsDecodedObject(w_str, encoding, None)
+ rffi.free_charp(encoding)
+ assert space.unwrap(w_res) == u"caf\xe9"
+
def test_eq(self, space, api):
assert 1 == api._PyString_Eq(space.wrap("hello"), space.wrap("hello"))
assert 0 == api._PyString_Eq(space.wrap("hello"), space.wrap("world"))
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
@@ -488,3 +488,55 @@
assert type(it) is type(iter([]))
assert module.tp_iternext(it) == 1
raises(StopIteration, module.tp_iternext, it)
+
+ def test_bool(self):
+ module = self.import_extension('foo', [
+ ("newInt", "METH_VARARGS",
+ """
+ IntLikeObject *intObj;
+ long intval;
+ PyObject *name;
+
+ if (!PyArg_ParseTuple(args, "i", &intval))
+ return NULL;
+
+ IntLike_Type.tp_as_number = &intlike_as_number;
+ intlike_as_number.nb_nonzero = intlike_nb_nonzero;
+ if (PyType_Ready(&IntLike_Type) < 0) return NULL;
+ intObj = PyObject_New(IntLikeObject, &IntLike_Type);
+ if (!intObj) {
+ return NULL;
+ }
+
+ intObj->value = intval;
+ return (PyObject *)intObj;
+ """)],
+ """
+ typedef struct
+ {
+ PyObject_HEAD
+ int value;
+ } IntLikeObject;
+
+ static int
+ intlike_nb_nonzero(IntLikeObject *v)
+ {
+ if (v->value == -42) {
+ PyErr_SetNone(PyExc_ValueError);
+ return -1;
+ }
+ return v->value;
+ }
+
+ PyTypeObject IntLike_Type = {
+ PyObject_HEAD_INIT(0)
+ /*ob_size*/ 0,
+ /*tp_name*/ "IntLike",
+ /*tp_basicsize*/ sizeof(IntLikeObject),
+ };
+ static PyNumberMethods intlike_as_number;
+ """)
+ assert not bool(module.newInt(0))
+ assert bool(module.newInt(1))
+ assert bool(module.newInt(-1))
+ raises(ValueError, bool, module.newInt(-42))
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -453,10 +453,22 @@
def test_tailmatch(self, space, api):
w_str = space.wrap(u"abcdef")
- assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 2, 10, 1) == 1
- assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 1, 5, -1) == 1
+ # prefix match
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 2, 9, -1) == 1
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 2, 4, -1) == 0 # ends at 'd'
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 1, 6, -1) == 0 # starts at 'b'
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cdf"), 2, 6, -1) == 0
+ # suffix match
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 1, 5, 1) == 1
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 3, 5, 1) == 0 # starts at 'd'
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 1, 6, 1) == 0 # ends at 'f'
+ assert api.PyUnicode_Tailmatch(w_str, space.wrap("bde"), 1, 5, 1) == 0
+ # type checks
self.raises(space, api, TypeError,
api.PyUnicode_Tailmatch, w_str, space.wrap(3), 2, 10, 1)
+ self.raises(space, api, TypeError,
+ api.PyUnicode_Tailmatch, space.wrap(3), space.wrap("abc"),
+ 2, 10, 1)
def test_count(self, space, api):
w_str = space.wrap(u"abcabdab")
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -593,7 +593,7 @@
suffix match), 0 otherwise. Return -1 if an error occurred."""
str = space.unicode_w(w_str)
substr = space.unicode_w(w_substr)
- if rffi.cast(lltype.Signed, direction) >= 0:
+ if rffi.cast(lltype.Signed, direction) <= 0:
return stringtype.stringstartswith(str, substr, start, end)
else:
return stringtype.stringendswith(str, substr, start, end)
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -5,6 +5,7 @@
interpleveldefs = {
'debug_repr': 'interp_extras.debug_repr',
'remove_invalidates': 'interp_extras.remove_invalidates',
+ 'set_invalidation': 'interp_extras.set_invalidation',
}
appleveldefs = {}
@@ -30,6 +31,7 @@
'isna': 'interp_numarray.isna',
'concatenate': 'interp_numarray.concatenate',
'repeat': 'interp_numarray.repeat',
+ 'where': 'interp_arrayops.where',
'set_string_function': 'appbridge.set_string_function',
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -10,6 +10,7 @@
from pypy.module.micronumpy.interp_dtype import get_dtype_cache
from pypy.module.micronumpy.interp_numarray import (Scalar, BaseArray,
scalar_w, W_NDimArray, array)
+from pypy.module.micronumpy.interp_arrayops import where
from pypy.module.micronumpy import interp_ufuncs
from pypy.rlib.objectmodel import specialize, instantiate
@@ -35,6 +36,7 @@
SINGLE_ARG_FUNCTIONS = ["sum", "prod", "max", "min", "all", "any",
"unegative", "flat", "tostring"]
TWO_ARG_FUNCTIONS = ["dot", 'take']
+THREE_ARG_FUNCTIONS = ['where']
class FakeSpace(object):
w_ValueError = None
@@ -445,14 +447,25 @@
arg = self.args[1].execute(interp)
if not isinstance(arg, BaseArray):
raise ArgumentNotAnArray
- if not isinstance(arg, BaseArray):
- raise ArgumentNotAnArray
if self.name == "dot":
w_res = arr.descr_dot(interp.space, arg)
elif self.name == 'take':
w_res = arr.descr_take(interp.space, arg)
else:
assert False # unreachable code
+ elif self.name in THREE_ARG_FUNCTIONS:
+ if len(self.args) != 3:
+ raise ArgumentMismatch
+ arg1 = self.args[1].execute(interp)
+ arg2 = self.args[2].execute(interp)
+ if not isinstance(arg1, BaseArray):
+ raise ArgumentNotAnArray
+ if not isinstance(arg2, BaseArray):
+ raise ArgumentNotAnArray
+ if self.name == "where":
+ w_res = where(interp.space, arr, arg1, arg2)
+ else:
+ assert False
else:
raise WrongFunctionName
if isinstance(w_res, BaseArray):
diff --git a/pypy/module/micronumpy/interp_arrayops.py b/pypy/module/micronumpy/interp_arrayops.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_arrayops.py
@@ -0,0 +1,90 @@
+
+from pypy.module.micronumpy.interp_numarray import convert_to_array,\
+ VirtualArray
+from pypy.module.micronumpy import signature
+
+class WhereArray(VirtualArray):
+ def __init__(self, space, arr, x, y):
+ self.arr = arr
+ self.x = x
+ self.y = y
+ VirtualArray.__init__(self, 'where', arr.shape[:],
+ x.find_dtype())
+
+ def create_sig(self):
+ if self.forced_result is not None:
+ return self.forced_result.create_sig()
+ return signature.WhereSignature(self.res_dtype, self.arr.find_dtype(),
+ self.arr.create_sig(),
+ self.x.create_sig(),
+ self.y.create_sig())
+
+ def _del_sources(self):
+ self.arr = None
+ self.x = None
+ self.y = None
+
+def where(space, w_arr, w_x, w_y):
+ """where(condition, [x, y])
+
+ Return elements, either from `x` or `y`, depending on `condition`.
+
+ If only `condition` is given, return ``condition.nonzero()``.
+
+ Parameters
+ ----------
+ condition : array_like, bool
+ When True, yield `x`, otherwise yield `y`.
+ x, y : array_like, optional
+ Values from which to choose. `x` and `y` need to have the same
+ shape as `condition`.
+
+ Returns
+ -------
+ out : ndarray or tuple of ndarrays
+ If both `x` and `y` are specified, the output array contains
+ elements of `x` where `condition` is True, and elements from
+ `y` elsewhere.
+
+ If only `condition` is given, return the tuple
+ ``condition.nonzero()``, the indices where `condition` is True.
+
+ See Also
+ --------
+ nonzero, choose
+
+ Notes
+ -----
+ If `x` and `y` are given and input arrays are 1-D, `where` is
+ equivalent to::
+
+ [xv if c else yv for (c,xv,yv) in zip(condition,x,y)]
+
+ Examples
+ --------
+ >>> np.where([[True, False], [True, True]],
+ ... [[1, 2], [3, 4]],
+ ... [[9, 8], [7, 6]])
+ array([[1, 8],
+ [3, 4]])
+
+ >>> np.where([[0, 1], [1, 0]])
+ (array([0, 1]), array([1, 0]))
+
+ >>> x = np.arange(9.).reshape(3, 3)
+ >>> np.where( x > 5 )
+ (array([2, 2, 2]), array([0, 1, 2]))
+ >>> x[np.where( x > 3.0 )] # Note: result is 1D.
+ array([ 4., 5., 6., 7., 8.])
+ >>> np.where(x < 5, x, -1) # Note: broadcasting.
+ array([[ 0., 1., 2.],
+ [ 3., 4., -1.],
+ [-1., -1., -1.]])
+
+
+ NOTE: support for not passing x and y is unsupported
+ """
+ arr = convert_to_array(space, w_arr)
+ x = convert_to_array(space, w_x)
+ y = convert_to_array(space, w_y)
+ return WhereArray(space, arr, x, y)
diff --git a/pypy/module/micronumpy/interp_extras.py b/pypy/module/micronumpy/interp_extras.py
--- a/pypy/module/micronumpy/interp_extras.py
+++ b/pypy/module/micronumpy/interp_extras.py
@@ -1,5 +1,5 @@
from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.micronumpy.interp_numarray import BaseArray
+from pypy.module.micronumpy.interp_numarray import BaseArray, get_numarray_cache
@unwrap_spec(array=BaseArray)
@@ -13,3 +13,7 @@
"""
del array.invalidates[:]
return space.w_None
+
+ at unwrap_spec(arg=bool)
+def set_invalidation(space, arg):
+ get_numarray_cache(space).enable_invalidation = arg
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -72,9 +72,10 @@
arr.force_if_needed()
del self.invalidates[:]
- def add_invalidates(self, other):
- self.invalidates.append(other)
-
+ def add_invalidates(self, space, other):
+ if get_numarray_cache(space).enable_invalidation:
+ self.invalidates.append(other)
+
def descr__new__(space, w_subtype, w_size, w_dtype=None):
dtype = space.interp_w(interp_dtype.W_Dtype,
space.call_function(space.gettypefor(interp_dtype.W_Dtype), w_dtype)
@@ -1583,3 +1584,10 @@
arr.fill(space, space.wrap(False))
return arr
return space.wrap(False)
+
+class NumArrayCache(object):
+ def __init__(self, space):
+ self.enable_invalidation = True
+
+def get_numarray_cache(space):
+ return space.fromcache(NumArrayCache)
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -278,7 +278,7 @@
else:
w_res = Call1(self.func, self.name, w_obj.shape, calc_dtype,
res_dtype, w_obj)
- w_obj.add_invalidates(w_res)
+ w_obj.add_invalidates(space, w_res)
return w_res
@@ -347,8 +347,8 @@
w_res = Call2(self.func, self.name,
new_shape, calc_dtype,
res_dtype, w_lhs, w_rhs, out)
- w_lhs.add_invalidates(w_res)
- w_rhs.add_invalidates(w_res)
+ w_lhs.add_invalidates(space, w_res)
+ w_rhs.add_invalidates(space, w_res)
if out:
w_res.get_concrete()
return w_res
diff --git a/pypy/module/micronumpy/signature.py b/pypy/module/micronumpy/signature.py
--- a/pypy/module/micronumpy/signature.py
+++ b/pypy/module/micronumpy/signature.py
@@ -498,3 +498,63 @@
arr.left.setitem(iterator.offset, value)
def debug_repr(self):
return 'AxisReduceSig(%s, %s)' % (self.name, self.right.debug_repr())
+
+class WhereSignature(Signature):
+ _immutable_fields_ = ['dtype', 'arrdtype', 'arrsig', 'xsig', 'ysig']
+
+ def __init__(self, dtype, arrdtype, arrsig, xsig, ysig):
+ self.dtype = dtype
+ self.arrdtype = arrdtype
+ self.arrsig = arrsig
+ self.xsig = xsig
+ self.ysig = ysig
+
+ def hash(self):
+ return (intmask(self.arrsig.hash() << 1) ^
+ intmask(self.xsig.hash() << 2) ^
+ intmask(self.ysig.hash() << 3))
+
+ def eq(self, other, compare_array_no=True):
+ if type(self) is not type(other):
+ return False
+ assert isinstance(other, WhereSignature)
+ return (self.arrsig.eq(other.arrsig, compare_array_no) and
+ self.xsig.eq(other.xsig, compare_array_no) and
+ self.ysig.eq(other.ysig, compare_array_no))
+
+ def _invent_array_numbering(self, arr, cache):
+ from pypy.module.micronumpy.interp_arrayops import WhereArray
+ assert isinstance(arr, WhereArray)
+ self.arrsig._invent_array_numbering(arr.arr, cache)
+ self.xsig._invent_array_numbering(arr.x, cache)
+ self.ysig._invent_array_numbering(arr.y, cache)
+
+ def _invent_numbering(self, cache, allnumbers):
+ self.arrsig._invent_numbering(cache, allnumbers)
+ self.xsig._invent_numbering(cache, allnumbers)
+ self.ysig._invent_numbering(cache, allnumbers)
+
+ def _create_iter(self, iterlist, arraylist, arr, transforms):
+ from pypy.module.micronumpy.interp_arrayops import WhereArray
+
+ assert isinstance(arr, WhereArray)
+ # XXX this does not support broadcasting correctly
+ self.arrsig._create_iter(iterlist, arraylist, arr.arr, transforms)
+ self.xsig._create_iter(iterlist, arraylist, arr.x, transforms)
+ self.ysig._create_iter(iterlist, arraylist, arr.y, transforms)
+
+ def eval(self, frame, arr):
+ from pypy.module.micronumpy.interp_arrayops import WhereArray
+ assert isinstance(arr, WhereArray)
+ lhs = self.xsig.eval(frame, arr.x).convert_to(self.dtype)
+ rhs = self.ysig.eval(frame, arr.y).convert_to(self.dtype)
+ w_val = self.arrsig.eval(frame, arr.arr)
+ if self.arrdtype.itemtype.bool(w_val):
+ return lhs
+ else:
+ return rhs
+
+ def debug_repr(self):
+ return 'Where(%s, %s, %s)' % (self.arrsig.debug_repr(),
+ self.xsig.debug_repr(),
+ self.ysig.debug_repr())
diff --git a/pypy/module/micronumpy/test/test_arrayops.py b/pypy/module/micronumpy/test/test_arrayops.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_arrayops.py
@@ -0,0 +1,16 @@
+
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+class AppTestNumSupport(BaseNumpyAppTest):
+ def test_where(self):
+ from _numpypy import where, ones, zeros, array
+ a = [1, 2, 3, 0, -3]
+ a = where(array(a) > 0, ones(5), zeros(5))
+ assert (a == [1, 1, 1, 0, 0]).all()
+
+ def test_where_invalidates(self):
+ from _numpypy import where, ones, zeros, array
+ a = array([1, 2, 3, 0, -3])
+ b = where(a > 0, ones(5), zeros(5))
+ a[0] = 0
+ assert (b == [1, 1, 1, 0, 0]).all()
diff --git a/pypy/module/micronumpy/test/test_compile.py b/pypy/module/micronumpy/test/test_compile.py
--- a/pypy/module/micronumpy/test/test_compile.py
+++ b/pypy/module/micronumpy/test/test_compile.py
@@ -270,3 +270,13 @@
b -> 2
""")
assert interp.results[0].value == 3
+
+ def test_where(self):
+ interp = self.run('''
+ a = [1, 0, 3, 0]
+ b = [1, 1, 1, 1]
+ c = [0, 0, 0, 0]
+ d = where(a, b, c)
+ d -> 1
+ ''')
+ assert interp.results[0].value == 0
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -1,9 +1,8 @@
import py
-from pypy.conftest import gettestobjspace, option
+from pypy.conftest import option
from pypy.interpreter.error import OperationError
-from pypy.module.micronumpy import signature
from pypy.module.micronumpy.appbridge import get_appbridge_cache
from pypy.module.micronumpy.interp_iter import Chunk, Chunks
from pypy.module.micronumpy.interp_numarray import W_NDimArray, shape_agreement
@@ -1831,6 +1830,19 @@
a[a & 1 == 1] = array([8, 9, 10])
assert (a == [[0, 8], [2, 9], [4, 10]]).all()
+ def test_array_indexing_bool_setitem_multidim(self):
+ from _numpypy import arange
+ a = arange(10).reshape(5, 2)
+ a[a & 1 == 0] = 15
+ assert (a == [[15, 1], [15, 3], [15, 5], [15, 7], [15, 9]]).all()
+
+ def test_array_indexing_bool_setitem_2(self):
+ from _numpypy import arange
+ a = arange(10).reshape(5, 2)
+ a = a[::2]
+ a[a & 1 == 0] = 15
+ assert (a == [[15, 1], [15, 5], [15, 9]]).all()
+
def test_copy_kwarg(self):
from _numpypy import array
x = array([1, 2, 3])
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -1,7 +1,6 @@
from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
-
class AppTestUfuncs(BaseNumpyAppTest):
def test_ufunc_instance(self):
from _numpypy import add, ufunc
@@ -149,7 +148,11 @@
assert math.isnan(fmax(0, nan))
assert math.isnan(fmax(nan, nan))
# The numpy docs specify that the FIRST NaN should be used if both are NaN
- assert math.copysign(1.0, fmax(nnan, nan)) == -1.0
+ # Since comparisons with nnan and nan all return false,
+ # use copysign on both sides to sidestep bug in nan representaion
+ # on Microsoft win32
+ assert math.copysign(1., fmax(nnan, nan)) == math.copysign(1., nnan)
+
def test_fmin(self):
from _numpypy import fmin
@@ -165,7 +168,9 @@
assert math.isnan(fmin(0, nan))
assert math.isnan(fmin(nan, nan))
# The numpy docs specify that the FIRST NaN should be used if both are NaN
- assert math.copysign(1.0, fmin(nnan, nan)) == -1.0
+ # use copysign on both sides to sidestep bug in nan representaion
+ # on Microsoft win32
+ assert math.copysign(1., fmin(nnan, nan)) == math.copysign(1., nnan)
def test_fmod(self):
from _numpypy import fmod
diff --git a/pypy/module/mmap/test/test_mmap.py b/pypy/module/mmap/test/test_mmap.py
--- a/pypy/module/mmap/test/test_mmap.py
+++ b/pypy/module/mmap/test/test_mmap.py
@@ -596,7 +596,7 @@
import sys
size = 0x14FFFFFFF
if sys.platform.startswith('win') or sys.platform == 'darwin':
- self.skip('test requires %s bytes and a long time to run' % size)
+ skip('test requires %s bytes and a long time to run' % size)
with open(self.tmpname, "w+b") as f:
f.seek(size)
@@ -618,7 +618,7 @@
import sys
size = 0x17FFFFFFF
if sys.platform.startswith('win') or sys.platform == 'darwin':
- self.skip('test requires %s bytes and a long time to run' % size)
+ skip('test requires %s bytes and a long time to run' % size)
with open(self.tmpname, "w+b") as f:
f.seek(size)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_exception.py b/pypy/module/pypyjit/test_pypy_c/test_exception.py
--- a/pypy/module/pypyjit/test_pypy_c/test_exception.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_exception.py
@@ -91,3 +91,29 @@
--TICK--
jump(..., descr=...)
""")
+
+ def test_continue_in_finally(self):
+ # check that 'continue' inside a try:finally: block is correctly
+ # detected as closing a loop
+ py.test.skip("is this case important?")
+ def f(n):
+ i = 0
+ while 1:
+ try:
+ if i < n:
+ continue
+ finally:
+ i += 1
+ return i
+
+ log = self.run(f, [2000])
+ assert log.result == 2001
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i40 = int_add_ovf(i31, 1)
+ guard_no_overflow(descr=...)
+ i41 = int_lt(i40, i33)
+ guard_true(i41, descr=...)
+ --TICK--
+ jump(..., descr=...)
+ """)
diff --git a/pypy/module/rctime/interp_time.py b/pypy/module/rctime/interp_time.py
--- a/pypy/module/rctime/interp_time.py
+++ b/pypy/module/rctime/interp_time.py
@@ -24,10 +24,9 @@
from pypy.module.thread import ll_thread as thread
eci = ExternalCompilationInfo(
+ includes = ['windows.h'],
post_include_bits = ["BOOL pypy_timemodule_setCtrlHandler(HANDLE event);"],
separate_module_sources=['''
- #include <windows.h>
-
static HANDLE interrupt_event;
static BOOL WINAPI CtrlHandlerRoutine(
diff --git a/pypy/module/zipimport/interp_zipimport.py b/pypy/module/zipimport/interp_zipimport.py
--- a/pypy/module/zipimport/interp_zipimport.py
+++ b/pypy/module/zipimport/interp_zipimport.py
@@ -229,7 +229,11 @@
startpos = fullname.rfind('.') + 1 # 0 when not found
assert startpos >= 0
subname = fullname[startpos:]
- return self.prefix + subname.replace('.', '/')
+ if ZIPSEP == os.path.sep:
+ return self.prefix + subname.replace('.', '/')
+ else:
+ return self.prefix.replace(os.path.sep, ZIPSEP) + \
+ subname.replace('.', '/')
def make_co_filename(self, filename):
"""
diff --git a/pypy/module/zipimport/test/test_zipimport.py b/pypy/module/zipimport/test/test_zipimport.py
--- a/pypy/module/zipimport/test/test_zipimport.py
+++ b/pypy/module/zipimport/test/test_zipimport.py
@@ -313,13 +313,11 @@
assert z.get_filename("package") == mod.__file__
def test_subdirectory_twice(self):
- import os, zipimport
+ #import os, zipimport
self.writefile("package/__init__.py", "")
self.writefile("package/subpackage/__init__.py", "")
self.writefile("package/subpackage/foo.py", "")
- import sys
- print sys.path
mod = __import__('package.subpackage.foo', None, None, [])
assert mod
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -414,7 +414,8 @@
def contains(space, w_container, w_item):
w_descr = space.lookup(w_container, '__contains__')
if w_descr is not None:
- return space.get_and_call_function(w_descr, w_container, w_item)
+ w_result = space.get_and_call_function(w_descr, w_container, w_item)
+ return space.nonzero(w_result)
return space._contains(w_container, w_item)
def _contains(space, w_container, w_item):
diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py
--- a/pypy/objspace/std/model.py
+++ b/pypy/objspace/std/model.py
@@ -366,7 +366,10 @@
__slots__ = ()
def __repr__(self):
- s = '%s(%s)' % (self.__class__.__name__, getattr(self, 'name', ''))
+ name = getattr(self, 'name', '')
+ if not isinstance(name, str):
+ name = ''
+ s = '%s(%s)' % (self.__class__.__name__, name)
w_cls = getattr(self, 'w__class__', None)
if w_cls is not None and w_cls is not self:
s += ' instance of %s' % self.w__class__
diff --git a/pypy/objspace/std/test/test_methodcache.py b/pypy/objspace/std/test/test_methodcache.py
--- a/pypy/objspace/std/test/test_methodcache.py
+++ b/pypy/objspace/std/test/test_methodcache.py
@@ -63,8 +63,31 @@
assert a.f() == 42 + i
A.f = eval("lambda self: %s" % (42 + i + 1, ))
cache_counter = __pypy__.method_cache_counter("f")
- # the cache hits come from A.f = ..., which first does a lookup on A as
- # well
+ #
+ # a bit of explanation about what's going on. (1) is the line "a.f()"
+ # and (2) is "A.f = ...".
+ #
+ # at line (1) we do the lookup on type(a).f
+ #
+ # at line (2) we do a setattr on A. However, descr_setattr does also a
+ # lookup of type(A).f i.e. type.f, to check if by chance 'f' is a data
+ # descriptor.
+ #
+ # At the first iteration:
+ # (1) is a miss because it's the first lookup of A.f. The result is cached
+ #
+ # (2) is a miss because it is the first lookup of type.f. The
+ # (non-existant) result is cached. The version of A changes, and 'f'
+ # is changed to be a cell object, so that subsequest assignments won't
+ # change the version of A
+ #
+ # At the second iteration:
+ # (1) is a miss because the version of A changed just before
+ # (2) is a hit, because type.f is cached. The version of A no longer changes
+ #
+ # At the third and subsequent iterations:
+ # (1) is a hit, because the version of A did not change
+ # (2) is a hit, see above
assert cache_counter == (17, 3)
def test_subclasses(self):
diff --git a/pypy/objspace/std/test/test_stringformat.py b/pypy/objspace/std/test/test_stringformat.py
--- a/pypy/objspace/std/test/test_stringformat.py
+++ b/pypy/objspace/std/test/test_stringformat.py
@@ -295,12 +295,11 @@
assert u'%x' % ten == 'a'
def test_long_no_overflow(self):
- big = 100000000000L
- assert "%x" % big == "174876e800"
+ big = 0x1234567890987654321
+ assert "%x" % big == "1234567890987654321"
def test_missing_cases(self):
- big = -123456789012345678901234567890L
- print '%032d' % big
+ big = -123456789012345678901234567890
assert '%032d' % big == '-0123456789012345678901234567890'
def test_invalid_char(self):
diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py
--- a/pypy/objspace/test/test_descroperation.py
+++ b/pypy/objspace/test/test_descroperation.py
@@ -694,5 +694,31 @@
l = len(X(X(2)))
assert l == 2 and type(l) is int
+ def test_bool___contains__(self):
+ class X(object):
+ def __contains__(self, item):
+ if item == 'foo':
+ return 42
+ else:
+ return 'hello world'
+ x = X()
+ res = 'foo' in x
+ assert res is True
+ res = 'bar' in x
+ assert res is True
+ #
+ class MyError(Exception):
+ pass
+ class CannotConvertToBool(object):
+ def __nonzero__(self):
+ raise MyError
+ class X(object):
+ def __contains__(self, item):
+ return CannotConvertToBool()
+ x = X()
+ raises(MyError, "'foo' in x")
+
+
+
class AppTestWithBuiltinShortcut(AppTest_Descroperation):
OPTIONS = {'objspace.std.builtinshortcut': True}
diff --git a/pypy/pytest.ini b/pypy/pytest.ini
--- a/pypy/pytest.ini
+++ b/pypy/pytest.ini
@@ -1,2 +1,2 @@
[pytest]
-addopts = --assert=plain -rf
+addopts = --assert=reinterp -rf
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -40,7 +40,7 @@
# In that case, do 5 bits at a time. The potential drawback is that
# a table of 2**5 intermediate results is computed.
-## FIVEARY_CUTOFF = 8 disabled for now
+FIVEARY_CUTOFF = 8
def _mask_digit(x):
@@ -456,7 +456,7 @@
# python adaptation: moved macros REDUCE(X) and MULT(X, Y, result)
# into helper function result = _help_mult(x, y, c)
- if 1: ## b.numdigits() <= FIVEARY_CUTOFF:
+ if b.numdigits() <= FIVEARY_CUTOFF:
# Left-to-right binary exponentiation (HAC Algorithm 14.79)
# http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
i = b.numdigits() - 1
@@ -469,30 +469,51 @@
z = _help_mult(z, a, c)
j >>= 1
i -= 1
-## else:
-## This code is disabled for now, because it assumes that
-## SHIFT is a multiple of 5. It could be fixed but it looks
-## like it's more troubles than benefits...
-##
-## # Left-to-right 5-ary exponentiation (HAC Algorithm 14.82)
-## # This is only useful in the case where c != None.
-## # z still holds 1L
-## table = [z] * 32
-## table[0] = z
-## for i in range(1, 32):
-## table[i] = _help_mult(table[i-1], a, c)
-## i = b.numdigits() - 1
-## while i >= 0:
-## bi = b.digit(i)
-## j = SHIFT - 5
-## while j >= 0:
-## index = (bi >> j) & 0x1f
-## for k in range(5):
-## z = _help_mult(z, z, c)
-## if index:
-## z = _help_mult(z, table[index], c)
-## j -= 5
-## i -= 1
+ else:
+ # Left-to-right 5-ary exponentiation (HAC Algorithm 14.82)
+ # This is only useful in the case where c != None.
+ # z still holds 1L
+ table = [z] * 32
+ table[0] = z
+ for i in range(1, 32):
+ table[i] = _help_mult(table[i-1], a, c)
+ i = b.numdigits()
+ # Note that here SHIFT is not a multiple of 5. The difficulty
+ # is to extract 5 bits at a time from 'b', starting from the
+ # most significant digits, so that at the end of the algorithm
+ # it falls exactly to zero.
+ # m = max number of bits = i * SHIFT
+ # m+ = m rounded up to the next multiple of 5
+ # j = (m+) % SHIFT = (m+) - (i * SHIFT)
+ # (computed without doing "i * SHIFT", which might overflow)
+ j = i % 5
+ if j != 0:
+ j = 5 - j
+ if not we_are_translated():
+ assert j == (i*SHIFT+4)//5*5 - i*SHIFT
+ #
+ accum = r_uint(0)
+ while True:
+ j -= 5
+ if j >= 0:
+ index = (accum >> j) & 0x1f
+ else:
+ # 'accum' does not have enough digit.
+ # must get the next digit from 'b' in order to complete
+ i -= 1
+ if i < 0:
+ break # done
+ bi = b.udigit(i)
+ index = ((accum << (-j)) | (bi >> (j+SHIFT))) & 0x1f
+ accum = bi
+ j += SHIFT
+ #
+ for k in range(5):
+ z = _help_mult(z, z, c)
+ if index:
+ z = _help_mult(z, table[index], c)
+ #
+ assert j == -5
if negativeOutput and z.sign != 0:
z = z.sub(c)
diff --git a/pypy/rlib/ropenssl.py b/pypy/rlib/ropenssl.py
--- a/pypy/rlib/ropenssl.py
+++ b/pypy/rlib/ropenssl.py
@@ -6,6 +6,7 @@
import sys, os
link_files = []
+testonly_libraries = []
if sys.platform == 'win32' and platform.name != 'mingw32':
libraries = ['libeay32', 'ssleay32',
'user32', 'advapi32', 'gdi32', 'msvcrt', 'ws2_32']
@@ -27,6 +28,7 @@
# amount of troubles due to symbol versions
# and 0.9.8/1.0.0
link_files += ['/usr/lib/libssl.a', '/usr/lib/libcrypto.a']
+ testonly_libraries += ['ssl', 'crypto']
else:
libraries += ['ssl', 'crypto']
@@ -41,6 +43,7 @@
eci = ExternalCompilationInfo(
libraries = libraries,
link_files = link_files,
+ testonly_libraries = testonly_libraries,
includes = includes,
export_symbols = [],
post_include_bits = [
diff --git a/pypy/rlib/test/test_rbigint.py b/pypy/rlib/test/test_rbigint.py
--- a/pypy/rlib/test/test_rbigint.py
+++ b/pypy/rlib/test/test_rbigint.py
@@ -379,6 +379,18 @@
for n, expected in [(37, 9), (1291, 931), (67889, 39464)]:
v = two.pow(t, rbigint.fromint(n))
assert v.toint() == expected
+ #
+ # more tests, comparing against CPython's answer
+ enabled = sample(range(5*32), 10)
+ for i in range(5*32):
+ t = t.mul(two) # add one random bit
+ if random() >= 0.5:
+ t = t.add(rbigint.fromint(1))
+ if i not in enabled:
+ continue # don't take forever
+ n = randint(1, sys.maxint)
+ v = two.pow(t, rbigint.fromint(n))
+ assert v.toint() == pow(2, t.tolong(), n)
def test_pow_lln(self):
x = 10L
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -1072,7 +1072,7 @@
try:
eci = _eci_cache[old_eci]
except KeyError:
- eci = old_eci.compile_shared_lib()
+ eci = old_eci.compile_shared_lib(ignore_a_files=True)
_eci_cache[old_eci] = eci
libraries = eci.testonly_libraries + eci.libraries + eci.frameworks
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -1168,8 +1168,11 @@
DIRENTP = lltype.Ptr(DIRENT)
os_opendir = self.llexternal('opendir', [rffi.CCHARP], DIRP,
compilation_info=compilation_info)
+ # XXX macro=True is hack to make sure we get the correct kind of
+ # dirent struct (which depends on defines)
os_readdir = self.llexternal('readdir', [DIRP], DIRENTP,
- compilation_info=compilation_info)
+ compilation_info=compilation_info,
+ macro=True)
os_closedir = self.llexternal('closedir', [DIRP], rffi.INT,
compilation_info=compilation_info)
diff --git a/pypy/rpython/module/test/test_ll_os.py b/pypy/rpython/module/test/test_ll_os.py
--- a/pypy/rpython/module/test/test_ll_os.py
+++ b/pypy/rpython/module/test/test_ll_os.py
@@ -4,6 +4,7 @@
import pypy
from pypy.tool.udir import udir
from pypy.translator.c.test.test_genc import compile
+from pypy.rpython.module import ll_os #has side effect of registering functions
from pypy.rpython import extregistry
import errno
diff --git a/pypy/tool/pytest/pypy_test_failure_demo.py b/pypy/tool/pytest/pypy_test_failure_demo.py
--- a/pypy/tool/pytest/pypy_test_failure_demo.py
+++ b/pypy/tool/pytest/pypy_test_failure_demo.py
@@ -8,6 +8,10 @@
def test_interp_func(space):
assert space.is_true(space.w_None)
+def test_interp_reinterpret(space):
+ a = 1
+ assert a == 2
+
class TestInterpTest:
def test_interp_method(self):
assert self.space.is_true(self.space.w_False)
diff --git a/pypy/translator/c/extfunc.py b/pypy/translator/c/extfunc.py
--- a/pypy/translator/c/extfunc.py
+++ b/pypy/translator/c/extfunc.py
@@ -5,7 +5,6 @@
from pypy.rpython.lltypesystem.rstr import STR, mallocstr
from pypy.rpython.lltypesystem import rstr
from pypy.rpython.lltypesystem import rlist
-from pypy.rpython.module import ll_time, ll_os
# table of functions hand-written in src/ll_*.h
# Note about *.im_func: The annotator and the rtyper expect direct
diff --git a/pypy/translator/c/src/exception.h b/pypy/translator/c/src/exception.h
--- a/pypy/translator/c/src/exception.h
+++ b/pypy/translator/c/src/exception.h
@@ -43,6 +43,16 @@
filename, lineno, functionname);
}
#endif
+#else /* !DO_LOG_EXC: define the function anyway, so that we can shut
+ off the prints of a debug_exc by remaking only testing_1.o */
+void RPyDebugReturnShowException(const char *msg, const char *filename,
+ long lineno, const char *functionname);
+#ifndef PYPY_NOT_MAIN_FILE
+void RPyDebugReturnShowException(const char *msg, const char *filename,
+ long lineno, const char *functionname)
+{
+}
+#endif
#endif /* DO_LOG_EXC */
/* Hint: functions and macros not defined here, like RPyRaiseException,
diff --git a/pypy/translator/tool/cbuild.py b/pypy/translator/tool/cbuild.py
--- a/pypy/translator/tool/cbuild.py
+++ b/pypy/translator/tool/cbuild.py
@@ -267,9 +267,12 @@
d['separate_module_files'] = ()
return files, ExternalCompilationInfo(**d)
- def compile_shared_lib(self, outputfilename=None):
+ def compile_shared_lib(self, outputfilename=None, ignore_a_files=False):
self = self.convert_sources_to_files()
- if not self.separate_module_files:
+ if ignore_a_files:
+ if not [fn for fn in self.link_files if fn.endswith('.a')]:
+ ignore_a_files = False # there are none
+ if not self.separate_module_files and not ignore_a_files:
if sys.platform != 'win32':
return self
if not self.export_symbols:
@@ -288,6 +291,13 @@
num += 1
basepath.ensure(dir=1)
outputfilename = str(pth.dirpath().join(pth.purebasename))
+
+ if ignore_a_files:
+ d = self._copy_attributes()
+ d['link_files'] = [fn for fn in d['link_files']
+ if not fn.endswith('.a')]
+ self = ExternalCompilationInfo(**d)
+
lib = str(host.compile([], self, outputfilename=outputfilename,
standalone=False))
d = self._copy_attributes()
More information about the pypy-commit
mailing list