Author: Ronan Lamy <[email protected]> Branch: py3k-update Changeset: r83999:f76e880e906f Date: 2016-04-28 04:52 +0100 http://bitbucket.org/pypy/pypy/changeset/f76e880e906f/
Log: hg merge default (broken) diff too long, truncating to 2000 out of 10591 lines diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +* python setup.py install in numpy does not somehow tell setuptools + it's installed (I bet it's about the py27 tag) +* reduce size of generated c code from slot definitions in slotdefs. diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst --- a/pypy/doc/cppyy.rst +++ b/pypy/doc/cppyy.rst @@ -12,9 +12,9 @@ The work on the cling backend has so far been done only for CPython, but bringing it to PyPy is a lot less work than developing it in the first place. -.. _Reflex: http://root.cern.ch/drupal/content/reflex -.. _CINT: http://root.cern.ch/drupal/content/cint -.. _cling: http://root.cern.ch/drupal/content/cling +.. _Reflex: https://root.cern.ch/how/how-use-reflex +.. _CINT: https://root.cern.ch/introduction-cint +.. _cling: https://root.cern.ch/cling .. _llvm: http://llvm.org/ .. _clang: http://clang.llvm.org/ @@ -283,7 +283,8 @@ core reflection set, but for the moment assume we want to have it in the reflection library that we are building for this example. -The ``genreflex`` script can be steered using a so-called `selection file`_, +The ``genreflex`` script can be steered using a so-called `selection file`_ +(see "Generating Reflex Dictionaries") which is a simple XML file specifying, either explicitly or by using a pattern, which classes, variables, namespaces, etc. to select from the given header file. @@ -305,7 +306,7 @@ <function name="BaseFactory" /> </lcgdict> -.. _selection file: http://root.cern.ch/drupal/content/generating-reflex-dictionaries +.. _selection file: https://root.cern.ch/how/how-use-reflex Now the reflection info can be generated and compiled:: @@ -811,7 +812,7 @@ immediately if you add ``$ROOTSYS/lib`` to the ``PYTHONPATH`` environment variable. -.. _PyROOT: http://root.cern.ch/drupal/content/pyroot +.. _PyROOT: https://root.cern.ch/pyroot There are a couple of minor differences between PyCintex and cppyy, most to do with naming. diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -79,7 +79,7 @@ :doc:`Full details <cppyy>` are `available here <cppyy>`. .. _installed separately: http://cern.ch/wlav/reflex-2013-08-14.tar.bz2 -.. _Reflex: http://root.cern.ch/drupal/content/reflex +.. _Reflex: https://root.cern.ch/how/how-use-reflex RPython Mixed Modules 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 @@ -22,3 +22,24 @@ JIT: use bitstrings to compress the lists of read or written descrs that we attach to EffectInfo. Fixes a problem we had in remove-objspace-options. + +.. branch: cpyext-for-merge +Update cpyext C-API support: + - allow c-snippet tests to be run with -A so we can verify we are compatible + - fix many edge cases exposed by fixing tests to run with -A + - issequence() logic matches cpython + - make PyStringObject and PyUnicodeObject field names compatible with cpython + - add prelminary support for PyDateTime_* + - support PyComplexObject, PyFloatObject, PyDict_Merge, PyDictProxy, + PyMemoryView_*, _Py_HashDouble, PyFile_AsFile, PyFile_FromFile, + - PyAnySet_CheckExact, PyUnicode_Concat + - improve support for PyGILState_Ensure, PyGILState_Release, and thread + primitives, also find a case where CPython will allow thread creation + before PyEval_InitThreads is run, dissallow on PyPy + - create a PyObject-specific list strategy + - rewrite slot assignment for typeobjects + - improve tracking of PyObject to rpython object mapping + - support tp_as_{number, sequence, mapping, buffer} slots +After this branch, we are almost able to support upstream numpy via cpyext, so +we created (yet another) fork of numpy at github.com/pypy/numpy with the needed +changes diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1171,7 +1171,27 @@ return self.wrap(self.lookup(w_obj, "__call__") is not None) def issequence_w(self, w_obj): - return (self.findattr(w_obj, self.wrap("__getitem__")) is not None) + if self.is_oldstyle_instance(w_obj): + return (self.findattr(w_obj, self.wrap('__getitem__')) is not None) + flag = self.type(w_obj).flag_map_or_seq + if flag == 'M': + return False + elif flag == 'S': + return True + else: + return (self.lookup(w_obj, '__getitem__') is not None) + + def ismapping_w(self, w_obj): + if self.is_oldstyle_instance(w_obj): + return (self.findattr(w_obj, self.wrap('__getitem__')) is not None) + flag = self.type(w_obj).flag_map_or_seq + if flag == 'M': + return True + elif flag == 'S': + return False + else: + return (self.lookup(w_obj, '__getitem__') is not None and + self.lookup(w_obj, '__getslice__') is None) # The code below only works # for the simple case (new-style instance). diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -36,6 +36,8 @@ from rpython.tool.sourcetools import func_with_new_name from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib import rawrefcount +from rpython.rlib import rthread +from rpython.rlib.debug import fatalerror_notb DEBUG_WRAPPER = True @@ -84,11 +86,13 @@ FILEP = rffi.COpaquePtr('FILE') if sys.platform == 'win32': - fileno = rffi.llexternal('_fileno', [FILEP], rffi.INT) + dash = '_' else: - fileno = rffi.llexternal('fileno', [FILEP], rffi.INT) - + dash = '' +fileno = rffi.llexternal(dash + 'fileno', [FILEP], rffi.INT) fopen = rffi.llexternal('fopen', [CONST_STRING, CONST_STRING], FILEP) +fdopen = rffi.llexternal(dash + 'fdopen', [rffi.INT, CONST_STRING], + FILEP, save_err=rffi.RFFI_SAVE_ERRNO) _fclose = rffi.llexternal('fclose', [FILEP], rffi.INT) def fclose(fp): @@ -118,9 +122,11 @@ def is_valid_fp(fp): return is_valid_fd(fileno(fp)) +pypy_decl = 'pypy_decl.h' + constant_names = """ Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GETCHARBUFFER -METH_COEXIST METH_STATIC METH_CLASS +METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES @@ -128,7 +134,7 @@ """.split() for name in constant_names: setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name)) -udir.join('pypy_decl.h').write("/* Will be filled later */\n") +udir.join(pypy_decl).write("/* Will be filled later */\n") udir.join('pypy_structmember_decl.h').write("/* Will be filled later */\n") udir.join('pypy_macros.h').write("/* Will be filled later */\n") globals().update(rffi_platform.configure(CConfig_constants)) @@ -144,7 +150,7 @@ target.chmod(0444) # make the file read-only, to make sure that nobody # edits it by mistake -def copy_header_files(dstdir): +def copy_header_files(dstdir, copy_numpy_headers): # XXX: 20 lines of code to recursively copy a directory, really?? assert dstdir.check(dir=True) headers = include_dir.listdir('*.h') + include_dir.listdir('*.inl') @@ -152,6 +158,18 @@ headers.append(udir.join(name)) _copy_header_files(headers, dstdir) + if copy_numpy_headers: + try: + dstdir.mkdir('numpy') + except py.error.EEXIST: + pass + numpy_dstdir = dstdir / 'numpy' + + numpy_include_dir = include_dir / 'numpy' + numpy_headers = numpy_include_dir.listdir('*.h') + numpy_include_dir.listdir('*.inl') + _copy_header_files(numpy_headers, numpy_dstdir) + + class NotSpecified(object): pass _NOT_SPECIFIED = NotSpecified() @@ -177,6 +195,61 @@ # exceptions generate a OperationError(w_SystemError); and the funtion returns # the error value specifed in the API. # +# Handling of the GIL +# ------------------- +# +# We add a global variable 'cpyext_glob_tid' that contains a thread +# id. Invariant: this variable always contain 0 when the PyPy GIL is +# released. It should also contain 0 when regular RPython code +# executes. In non-cpyext-related code, it will thus always be 0. +# +# **make_generic_cpy_call():** RPython to C, with the GIL held. Before +# the call, must assert that the global variable is 0 and set the +# current thread identifier into the global variable. After the call, +# assert that the global variable still contains the current thread id, +# and reset it to 0. +# +# **make_wrapper():** C to RPython; by default assume that the GIL is +# held, but accepts gil="acquire", "release", "around", +# "pygilstate_ensure", "pygilstate_release". +# +# When a wrapper() is called: +# +# * "acquire": assert that the GIL is not currently held, i.e. the +# global variable does not contain the current thread id (otherwise, +# deadlock!). Acquire the PyPy GIL. After we acquired it, assert +# that the global variable is 0 (it must be 0 according to the +# invariant that it was 0 immediately before we acquired the GIL, +# because the GIL was released at that point). +# +# * gil=None: we hold the GIL already. Assert that the current thread +# identifier is in the global variable, and replace it with 0. +# +# * "pygilstate_ensure": if the global variable contains the current +# thread id, replace it with 0 and set the extra arg to 0. Otherwise, +# do the "acquire" and set the extra arg to 1. Then we'll call +# pystate.py:PyGILState_Ensure() with this extra arg, which will do +# the rest of the logic. +# +# When a wrapper() returns, first assert that the global variable is +# still 0, and then: +# +# * "release": release the PyPy GIL. The global variable was 0 up to +# and including at the point where we released the GIL, but afterwards +# it is possible that the GIL is acquired by a different thread very +# quickly. +# +# * gil=None: we keep holding the GIL. Set the current thread +# identifier into the global variable. +# +# * "pygilstate_release": if the argument is PyGILState_UNLOCKED, +# release the PyPy GIL; otherwise, set the current thread identifier +# into the global variable. The rest of the logic of +# PyGILState_Release() should be done before, in pystate.py. + +cpyext_glob_tid_ptr = lltype.malloc(rffi.CArray(lltype.Signed), 1, + flavor='raw', immortal=True, zero=True) + cpyext_namespace = NameManager('cpyext_') @@ -196,6 +269,9 @@ argnames, varargname, kwargname = pycode.cpython_code_signature(callable.func_code) assert argnames[0] == 'space' + if gil == 'pygilstate_ensure': + assert argnames[-1] == 'previous_state' + del argnames[-1] self.argnames = argnames[1:] assert len(self.argnames) == len(self.argtypes) self.gil = gil @@ -416,15 +492,14 @@ 'PyThread_acquire_lock', 'PyThread_release_lock', 'PyThread_create_key', 'PyThread_delete_key', 'PyThread_set_key_value', 'PyThread_get_key_value', 'PyThread_delete_key_value', - 'PyThread_ReInitTLS', + 'PyThread_ReInitTLS', 'PyThread_init_thread', + 'PyThread_start_new_thread', 'PyStructSequence_InitType', 'PyStructSequence_New', 'PyStructSequence_UnnamedField', 'PyFunction_Type', 'PyMethod_Type', 'PyRange_Type', 'PyTraceBack_Type', - 'PyArray_Type', '_PyArray_FILLWBYTE', '_PyArray_ZEROS', '_PyArray_CopyInto', - 'Py_DebugFlag', 'Py_VerboseFlag', 'Py_InteractiveFlag', 'Py_InspectFlag', 'Py_OptimizeFlag', 'Py_NoSiteFlag', 'Py_BytesWarningFlag', 'Py_UseClassExceptionsFlag', 'Py_FrozenFlag', 'Py_TabcheckFlag', 'Py_UnicodeFlag', 'Py_IgnoreEnvironmentFlag', @@ -433,11 +508,11 @@ ] TYPES = {} GLOBALS = { # this needs to include all prebuilt pto, otherwise segfaults occur - '_Py_NoneStruct#': ('PyObject*', 'space.w_None'), - '_Py_TrueStruct#': ('PyObject*', 'space.w_True'), - '_Py_ZeroStruct#': ('PyObject*', 'space.w_False'), - '_Py_NotImplementedStruct#': ('PyObject*', 'space.w_NotImplemented'), - '_Py_EllipsisObject#': ('PyObject*', 'space.w_Ellipsis'), + '_Py_NoneStruct#%s' % pypy_decl: ('PyObject*', 'space.w_None'), + '_Py_TrueStruct#%s' % pypy_decl: ('PyObject*', 'space.w_True'), + '_Py_ZeroStruct#%s' % pypy_decl: ('PyObject*', 'space.w_False'), + '_Py_NotImplementedStruct#%s' % pypy_decl: ('PyObject*', 'space.w_NotImplemented'), + '_Py_EllipsisObject#%s' % pypy_decl: ('PyObject*', 'space.w_Ellipsis'), 'PyDateTimeAPI': ('PyDateTime_CAPI*', 'None'), } FORWARD_DECLS = [] @@ -466,6 +541,7 @@ "PyBytes_Type": "space.w_bytes", "PyUnicode_Type": "space.w_unicode", "PyDict_Type": "space.w_dict", + "PyDictProxy_Type": 'space.gettypeobject(cpyext.dictproxyobject.W_DictProxyObject.typedef)', "PyTuple_Type": "space.w_tuple", "PyList_Type": "space.w_list", "PySet_Type": "space.w_set", @@ -488,7 +564,7 @@ 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', 'PyInstanceMethod_Type': 'space.gettypeobject(cpyext.classobject.InstanceMethod.typedef)', }.items(): - GLOBALS['%s#' % (cpyname, )] = ('PyTypeObject*', pypyexpr) + GLOBALS['%s#%s' % (cpyname, pypy_decl)] = ('PyTypeObject*', pypyexpr) for cpyname in '''PyMethodObject PyListObject PyLongObject PyDictObject'''.split(): @@ -604,7 +680,14 @@ fatal_value = callable.api_func.restype._defl() gil_acquire = (gil == "acquire" or gil == "around") gil_release = (gil == "release" or gil == "around") - assert gil is None or gil_acquire or gil_release + pygilstate_ensure = (gil == "pygilstate_ensure") + pygilstate_release = (gil == "pygilstate_release") + assert (gil is None or gil_acquire or gil_release + or pygilstate_ensure or pygilstate_release) + deadlock_error = ("GIL deadlock detected when a CPython C extension " + "module calls %r" % (callable.__name__,)) + no_gil_error = ("GIL not held when a CPython C extension " + "module calls %r" % (callable.__name__,)) @specialize.ll() def wrapper(*args): @@ -612,8 +695,27 @@ from pypy.module.cpyext.pyobject import as_pyobj # we hope that malloc removal removes the newtuple() that is # inserted exactly here by the varargs specializer + + # see "Handling of the GIL" above (careful, we don't have the GIL here) + tid = rthread.get_or_make_ident() if gil_acquire: + if cpyext_glob_tid_ptr[0] == tid: + fatalerror_notb(deadlock_error) rgil.acquire() + assert cpyext_glob_tid_ptr[0] == 0 + elif pygilstate_ensure: + from pypy.module.cpyext import pystate + if cpyext_glob_tid_ptr[0] == tid: + cpyext_glob_tid_ptr[0] = 0 + args += (pystate.PyGILState_LOCKED,) + else: + rgil.acquire() + args += (pystate.PyGILState_UNLOCKED,) + else: + if cpyext_glob_tid_ptr[0] != tid: + fatalerror_notb(no_gil_error) + cpyext_glob_tid_ptr[0] = 0 + rffi.stackcounter.stacks_counter += 1 llop.gc_stack_bottom(lltype.Void) # marker for trackgcroot.py retval = fatal_value @@ -622,7 +724,8 @@ try: if not we_are_translated() and DEBUG_WRAPPER: print >>sys.stderr, callable, - assert len(args) == len(callable.api_func.argtypes) + assert len(args) == (len(callable.api_func.argtypes) + + pygilstate_ensure) for i, (typ, is_wrapped) in argtypes_enum_ui: arg = args[i] if is_PyObject(typ) and is_wrapped: @@ -631,6 +734,8 @@ else: arg_conv = arg boxed_args += (arg_conv, ) + if pygilstate_ensure: + boxed_args += (args[-1], ) state = space.fromcache(State) try: result = callable(space, *boxed_args) @@ -690,8 +795,20 @@ pypy_debug_catch_fatal_exception() assert False rffi.stackcounter.stacks_counter -= 1 - if gil_release: + + # see "Handling of the GIL" above + assert cpyext_glob_tid_ptr[0] == 0 + if pygilstate_release: + from pypy.module.cpyext import pystate + arg = rffi.cast(lltype.Signed, args[-1]) + unlock = (arg == pystate.PyGILState_UNLOCKED) + else: + unlock = gil_release + if unlock: rgil.release() + else: + cpyext_glob_tid_ptr[0] = tid + return retval callable._always_inline_ = 'try' wrapper.__name__ = "wrapper for %r" % (callable, ) @@ -784,6 +901,9 @@ structindex = {} for header, header_functions in FUNCTIONS_BY_HEADER.iteritems(): for name, func in header_functions.iteritems(): + if not func: + # added only for the macro, not the decl + continue restype, args = c_function_signature(db, func) members.append('%s (*%s)(%s);' % (restype, name, args)) structindex[name] = len(structindex) @@ -795,12 +915,12 @@ RPY_EXTERN struct PyPyAPI* pypyAPI = &_pypyAPI; """ % dict(members=structmembers) - functions = generate_decls_and_callbacks(db, export_symbols, + functions = generate_decls_and_callbacks(db, export_symbols, prefix='cpyexttest') global_objects = [] for name, (typ, expr) in GLOBALS.iteritems(): - if "#" in name: + if '#' in name: continue if typ == 'PyDateTime_CAPI*': continue @@ -824,7 +944,7 @@ '\n' + '\n'.join(functions)) - eci = build_eci(True, export_symbols, code) + eci = build_eci(True, export_symbols, code, use_micronumpy) eci = eci.compile_shared_lib( outputfilename=str(udir / "module_cache" / "pypyapi")) modulename = py.path.local(eci.libraries[-1]) @@ -836,7 +956,7 @@ ob = rawrefcount.next_dead(PyObject) if not ob: break - print ob + print 'deallocating PyObject', ob decref(space, ob) print 'dealloc_trigger DONE' return "RETRY" @@ -855,8 +975,8 @@ for name, (typ, expr) in GLOBALS.iteritems(): from pypy.module import cpyext # for the eval() below w_obj = eval(expr) - if name.endswith('#'): - name = name[:-1] + if '#' in name: + name = name.split('#')[0] isptr = False else: isptr = True @@ -901,7 +1021,7 @@ # ctypes.c_void_p) for header, header_functions in FUNCTIONS_BY_HEADER.iteritems(): for name, func in header_functions.iteritems(): - if name.startswith('cpyext_'): # XXX hack + if name.startswith('cpyext_') or func is None: # XXX hack continue pypyAPI[structindex[name]] = ctypes.cast( ll2ctypes.lltype2ctypes(func.get_llhelper(space)), @@ -954,6 +1074,8 @@ cpyext_type_init = self.cpyext_type_init self.cpyext_type_init = None for pto, w_type in cpyext_type_init: + if space.is_w(w_type, space.w_str): + pto.c_tp_itemsize = 1 finish_type_1(space, pto) finish_type_2(space, pto, w_type) @@ -971,10 +1093,14 @@ pypy_macros = [] renamed_symbols = [] for name in export_symbols: - name = name.replace("#", "") + if '#' in name: + name,header = name.split('#') + else: + header = pypy_decl newname = mangle_name(prefix, name) assert newname, name - pypy_macros.append('#define %s %s' % (name, newname)) + if header == pypy_decl: + pypy_macros.append('#define %s %s' % (name, newname)) if name.startswith("PyExc_"): pypy_macros.append('#define _%s _%s' % (name, newname)) renamed_symbols.append(newname) @@ -1003,7 +1129,7 @@ # implement function callbacks and generate function decls functions = [] decls = {} - pypy_decls = decls['pypy_decl.h'] = [] + pypy_decls = decls[pypy_decl] = [] pypy_decls.append('#define Signed long /* xxx temporary fix */\n') pypy_decls.append('#define Unsigned unsigned long /* xxx temporary fix */\n') @@ -1019,6 +1145,8 @@ header = decls[header_name] for name, func in sorted(header_functions.iteritems()): + if not func: + continue if header == DEFAULT_HEADER: _name = name else: @@ -1044,12 +1172,15 @@ functions.append(header + '\n{return va_arg(*vp, %s);}\n' % name) for name, (typ, expr) in GLOBALS.iteritems(): - if name.endswith('#'): - name = name.replace("#", "") + if '#' in name: + name, header = name.split("#") typ = typ.replace("*", "") elif name.startswith('PyExc_'): typ = 'PyObject*' - pypy_decls.append('PyAPI_DATA(%s) %s;' % (typ, name)) + header = pypy_decl + if header != pypy_decl: + decls[header].append('#define %s %s' % (name, mangle_name(prefix, name))) + decls[header].append('PyAPI_DATA(%s) %s;' % (typ, name)) for header_name in FUNCTIONS_BY_HEADER.keys(): header = decls[header_name] @@ -1076,9 +1207,10 @@ source_dir / "pysignals.c", source_dir / "pythread.c", source_dir / "missing.c", + source_dir / "pymem.c", ] -def build_eci(building_bridge, export_symbols, code): +def build_eci(building_bridge, export_symbols, code, use_micronumpy=False): "NOT_RPYTHON" # Build code and get pointer to the structure kwds = {} @@ -1100,9 +1232,11 @@ # Generate definitions for global structures structs = ["#include <Python.h>"] + if use_micronumpy: + structs.append('#include <pypy_numpy.h> /* api.py line 1223 */') for name, (typ, expr) in GLOBALS.iteritems(): - if name.endswith('#'): - structs.append('%s %s;' % (typ[:-1], name[:-1])) + if '#' in name: + structs.append('%s %s;' % (typ[:-1], name.split('#')[0])) elif name.startswith('PyExc_'): structs.append('PyTypeObject _%s;' % (name,)) structs.append('PyObject* %s = (PyObject*)&_%s;' % (name, name)) @@ -1146,11 +1280,12 @@ use_micronumpy = space.config.objspace.usemodules.micronumpy if not use_micronumpy: return use_micronumpy - # import to register api functions by side-effect - import pypy.module.cpyext.ndarrayobject - global GLOBALS, SYMBOLS_C, separate_module_files - GLOBALS["PyArray_Type#"]= ('PyTypeObject*', "space.gettypeobject(W_NDimArray.typedef)") - SYMBOLS_C += ['PyArray_Type', '_PyArray_FILLWBYTE', '_PyArray_ZEROS'] + # import registers api functions by side-effect, we also need HEADER + from pypy.module.cpyext.ndarrayobject import HEADER + global GLOBALS, FUNCTIONS_BY_HEADER, separate_module_files + for func_name in ['PyArray_Type', '_PyArray_FILLWBYTE', '_PyArray_ZEROS']: + FUNCTIONS_BY_HEADER.setdefault(HEADER, {})[func_name] = None + GLOBALS["PyArray_Type#%s" % HEADER] = ('PyTypeObject*', "space.gettypeobject(W_NDimArray.typedef)") separate_module_files.append(source_dir / "ndarrayobject.c") return use_micronumpy @@ -1160,14 +1295,18 @@ export_symbols = sorted(FUNCTIONS) + sorted(SYMBOLS_C) + sorted(GLOBALS) from rpython.translator.c.database import LowLevelDatabase db = LowLevelDatabase() + prefix = 'PyPy' - generate_macros(export_symbols, prefix='PyPy') + generate_macros(export_symbols, prefix=prefix) - functions = generate_decls_and_callbacks(db, [], api_struct=False, - prefix='PyPy') - code = "#include <Python.h>\n" + "\n".join(functions) + functions = generate_decls_and_callbacks(db, [], api_struct=False, + prefix=prefix) + code = "#include <Python.h>\n" + if use_micronumpy: + code += "#include <pypy_numpy.h> /* api.py line 1290 */" + code += "\n".join(functions) - eci = build_eci(False, export_symbols, code) + eci = build_eci(False, export_symbols, code, use_micronumpy) space.fromcache(State).install_dll(eci) @@ -1179,9 +1318,14 @@ lines = ['PyObject *pypy_static_pyobjs[] = {\n'] include_lines = ['RPY_EXTERN PyObject *pypy_static_pyobjs[];\n'] for name, (typ, expr) in sorted(GLOBALS.items()): - if name.endswith('#'): + if '#' in name: + name, header = name.split('#') assert typ in ('PyObject*', 'PyTypeObject*', 'PyIntObject*') - typ, name = typ[:-1], name[:-1] + typ = typ[:-1] + if header != pypy_decl: + # since the #define is not in pypy_macros, do it here + mname = mangle_name(prefix, name) + include_lines.append('#define %s %s\n' % (name, mname)) elif name.startswith('PyExc_'): typ = 'PyTypeObject' name = '_' + name @@ -1208,14 +1352,16 @@ for header, header_functions in FUNCTIONS_BY_HEADER.iteritems(): for name, func in header_functions.iteritems(): + if not func: + continue newname = mangle_name('PyPy', name) or name - deco = entrypoint_lowlevel("cpyext", func.argtypes, newname, + deco = entrypoint_lowlevel("cpyext", func.argtypes, newname, relax=True) deco(func.get_wrapper(space)) setup_init_functions(eci, translating=True) trunk_include = pypydir.dirpath() / 'include' - copy_header_files(trunk_include) + copy_header_files(trunk_include, use_micronumpy) def init_static_data_translated(space): builder = space.fromcache(StaticObjectBuilder) @@ -1352,10 +1498,17 @@ arg = as_pyobj(space, arg) boxed_args += (arg,) + # see "Handling of the GIL" above + tid = rthread.get_ident() + assert cpyext_glob_tid_ptr[0] == 0 + cpyext_glob_tid_ptr[0] = tid + try: # Call the function result = call_external_function(func, *boxed_args) finally: + assert cpyext_glob_tid_ptr[0] == tid + cpyext_glob_tid_ptr[0] = 0 keepalive_until_here(*keepalives) if is_PyObject(RESULT_TYPE): diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py --- a/pypy/module/cpyext/bytesobject.py +++ b/pypy/module/cpyext/bytesobject.py @@ -2,11 +2,11 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( cpython_api, cpython_struct, bootstrap_function, build_type_checkers, - PyObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL) + PyObjectFields, PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL) from pypy.module.cpyext.pyerrors import PyErr_BadArgument from pypy.module.cpyext.pyobject import ( PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference, - make_typedescr, get_typedescr) + make_typedescr, get_typedescr, as_pyobj, Py_IncRef) ## ## Implementation of PyBytesObject @@ -27,7 +27,7 @@ ## Solution ## -------- ## -## PyBytesObject contains two additional members: the size and a pointer to a +## PyBytesObject contains two additional members: the ob_size and a pointer to a ## char buffer; it may be NULL. ## ## - A string allocated by pypy will be converted into a PyBytesObject with a @@ -36,7 +36,7 @@ ## ## - A string allocated with PyBytes_FromStringAndSize(NULL, size) will ## allocate a PyBytesObject structure, and a buffer with the specified -## size, but the reference won't be stored in the global map; there is no +## size+1, but the reference won't be stored in the global map; there is no ## corresponding object in pypy. When from_ref() or Py_INCREF() is called, ## the pypy string is created, and added to the global map of tracked ## objects. The buffer is then supposed to be immutable. @@ -52,8 +52,8 @@ PyBytesObjectStruct = lltype.ForwardReference() PyBytesObject = lltype.Ptr(PyBytesObjectStruct) -PyBytesObjectFields = PyObjectFields + \ - (("buffer", rffi.CCHARP), ("size", Py_ssize_t)) +PyBytesObjectFields = PyVarObjectFields + \ + (("ob_shash", rffi.LONG), ("ob_sstate", rffi.INT), ("buffer", rffi.CCHARP)) cpython_struct("PyBytesObject", PyBytesObjectFields, PyBytesObjectStruct) @bootstrap_function @@ -78,10 +78,11 @@ py_str = rffi.cast(PyBytesObject, py_obj) buflen = length + 1 - py_str.c_size = length + py_str.c_ob_size = length py_str.c_buffer = lltype.malloc(rffi.CCHARP.TO, buflen, flavor='raw', zero=True, add_memory_pressure=True) + py_str.c_ob_sstate = rffi.cast(rffi.INT, 0) # SSTATE_NOT_INTERNED return py_str def bytes_attach(space, py_obj, w_obj): @@ -90,8 +91,10 @@ buffer must not be modified. """ py_str = rffi.cast(PyBytesObject, py_obj) - py_str.c_size = len(space.bytes_w(w_obj)) + py_str.c_ob_size = len(space.str_w(w_obj)) py_str.c_buffer = lltype.nullptr(rffi.CCHARP.TO) + py_str.c_ob_shash = space.hash_w(w_obj) + py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL def bytes_realize(space, py_obj): """ @@ -99,8 +102,13 @@ be modified after this call. """ py_str = rffi.cast(PyBytesObject, py_obj) - s = rffi.charpsize2str(py_str.c_buffer, py_str.c_size) + if not py_str.c_buffer: + py_str.c_buffer = lltype.malloc(rffi.CCHARP.TO, py_str.c_ob_size + 1, + flavor='raw', zero=True) + s = rffi.charpsize2str(py_str.c_buffer, py_str.c_ob_size) w_obj = space.wrapbytes(s) + py_str.c_ob_shash = space.hash_w(w_obj) + py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL track_reference(space, py_obj, w_obj) return w_obj @@ -157,12 +165,12 @@ ref_str.c_buffer = rffi.str2charp(s) buffer[0] = ref_str.c_buffer if length: - length[0] = ref_str.c_size + length[0] = ref_str.c_ob_size else: i = 0 while ref_str.c_buffer[i] != '\0': i += 1 - if i != ref_str.c_size: + if i != ref_str.c_ob_size: raise OperationError(space.w_TypeError, space.wrap( "expected string without null bytes")) return 0 @@ -171,7 +179,7 @@ def PyBytes_Size(space, ref): if from_ref(space, rffi.cast(PyObject, ref.c_ob_type)) is space.w_str: ref = rffi.cast(PyBytesObject, ref) - return ref.c_size + return ref.c_ob_size else: w_obj = from_ref(space, ref) return space.len_w(w_obj) @@ -200,7 +208,7 @@ ref[0] = lltype.nullptr(PyObject.TO) raise to_cp = newsize - oldsize = py_str.c_size + oldsize = py_str.c_ob_size if oldsize < newsize: to_cp = oldsize for i in range(to_cp): @@ -231,8 +239,8 @@ return w_str = from_ref(space, ref[0]) w_newstr = space.add(w_str, w_newpart) - Py_DecRef(space, ref[0]) ref[0] = make_ref(space, w_newstr) + Py_IncRef(space, ref[0]) @cpython_api([PyObjectP, PyObject], lltype.Void) def PyBytes_ConcatAndDel(space, ref, newpart): diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -42,9 +42,15 @@ return datetimeAPI -PyDateTime_Date = PyObject -PyDateTime_Time = PyObject -PyDateTime_DateTime = PyObject +PyDateTime_DateStruct = lltype.ForwardReference() +PyDateTime_TimeStruct = lltype.ForwardReference() +PyDateTime_DateTimeStruct = lltype.ForwardReference() +cpython_struct("PyDateTime_Date", PyObjectFields, PyDateTime_DateStruct) +PyDateTime_Date = lltype.Ptr(PyDateTime_DateStruct) +cpython_struct("PyDateTime_Time", PyObjectFields, PyDateTime_TimeStruct) +PyDateTime_Time = lltype.Ptr(PyDateTime_TimeStruct) +cpython_struct("PyDateTime_DateTime", PyObjectFields, PyDateTime_DateTimeStruct) +PyDateTime_DateTime = lltype.Ptr(PyDateTime_DateTimeStruct) PyDeltaObjectStruct = lltype.ForwardReference() cpython_struct("PyDateTime_Delta", PyObjectFields, PyDeltaObjectStruct) diff --git a/pypy/module/cpyext/complexobject.py b/pypy/module/cpyext/complexobject.py --- a/pypy/module/cpyext/complexobject.py +++ b/pypy/module/cpyext/complexobject.py @@ -1,16 +1,51 @@ from rpython.rtyper.lltypesystem import lltype, rffi -from pypy.module.cpyext.api import ( +from pypy.module.cpyext.api import (PyObjectFields, bootstrap_function, cpython_api, cpython_struct, PyObject, build_type_checkers) +from pypy.module.cpyext.pyobject import ( + make_typedescr, track_reference, from_ref) from pypy.module.cpyext.floatobject import PyFloat_AsDouble from pypy.objspace.std.complexobject import W_ComplexObject from pypy.interpreter.error import OperationError PyComplex_Check, PyComplex_CheckExact = build_type_checkers("Complex") -Py_complex_t = lltype.ForwardReference() +Py_complex_t = rffi.CStruct('Py_complex_t', + ('real', rffi.DOUBLE), + ('imag', rffi.DOUBLE), + hints={'size': 2 * rffi.sizeof(rffi.DOUBLE)}) Py_complex_ptr = lltype.Ptr(Py_complex_t) -Py_complex_fields = (("real", rffi.DOUBLE), ("imag", rffi.DOUBLE)) -cpython_struct("Py_complex", Py_complex_fields, Py_complex_t) + +PyComplexObjectStruct = lltype.ForwardReference() +PyComplexObject = lltype.Ptr(PyComplexObjectStruct) +PyComplexObjectFields = PyObjectFields + \ + (("cval", Py_complex_t),) +cpython_struct("PyComplexObject", PyComplexObjectFields, PyComplexObjectStruct) + +@bootstrap_function +def init_complexobject(space): + "Type description of PyComplexObject" + make_typedescr(space.w_complex.layout.typedef, + basestruct=PyComplexObject.TO, + attach=complex_attach, + realize=complex_realize) + +def complex_attach(space, py_obj, w_obj): + """ + Fills a newly allocated PyComplexObject with the given complex object. The + value must not be modified. + """ + assert isinstance(w_obj, W_ComplexObject) + py_obj = rffi.cast(PyComplexObject, py_obj) + py_obj.c_cval.c_real = w_obj.realval + py_obj.c_cval.c_imag = w_obj.imagval + +def complex_realize(space, obj): + py_obj = rffi.cast(PyComplexObject, obj) + w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) + w_obj = space.allocate_instance(W_ComplexObject, w_type) + w_obj.__init__(py_obj.c_cval.c_real, py_obj.c_cval.c_imag) + track_reference(space, obj, w_obj) + return w_obj @cpython_api([lltype.Float, lltype.Float], PyObject) diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -23,6 +23,7 @@ # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. + # XXX this is wrong with IntMutableCell. Hope it works... return w_res @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) @@ -62,6 +63,7 @@ # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. + # XXX this is wrong with IntMutableCell. Hope it works... return w_res @cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=-1) @@ -104,6 +106,32 @@ """ return space.call_method(space.w_dict, "copy", w_obj) +def _has_val(space, w_dict, w_key): + try: + w_val = space.getitem(w_dict, w_key) + except OperationError as e: + if e.match(space, space.w_KeyError): + return False + else: + raise + return True + +@cpython_api([PyObject, PyObject, rffi.INT_real], rffi.INT_real, error=-1) +def PyDict_Merge(space, w_a, w_b, override): + """Iterate over mapping object b adding key-value pairs to dictionary a. + b may be a dictionary, or any object supporting PyMapping_Keys() + and PyObject_GetItem(). If override is true, existing pairs in a + will be replaced if a matching key is found in b, otherwise pairs will + only be added if there is not a matching key in a. Return 0 on + success or -1 if an exception was raised. + """ + override = rffi.cast(lltype.Signed, override) + w_keys = space.call_method(w_b, "keys") + for w_key in space.iteriterable(w_keys): + if not _has_val(space, w_a, w_key) or override != 0: + space.setitem(w_a, w_key, space.getitem(w_b, w_key)) + return 0 + @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_Update(space, w_obj, w_other): """This is the same as PyDict_Merge(a, b, 1) in C, or a.update(b) in diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py --- a/pypy/module/cpyext/floatobject.py +++ b/pypy/module/cpyext/floatobject.py @@ -1,8 +1,42 @@ from rpython.rtyper.lltypesystem import rffi, lltype -from pypy.module.cpyext.api import ( +from pypy.module.cpyext.api import (PyObjectFields, bootstrap_function, + cpython_struct, CANNOT_FAIL, cpython_api, PyObject, build_type_checkers, CONST_STRING) +from pypy.module.cpyext.pyobject import ( + make_typedescr, track_reference, from_ref) from pypy.interpreter.error import OperationError from rpython.rlib.rstruct import runpack +from pypy.objspace.std.floatobject import W_FloatObject + +PyFloatObjectStruct = lltype.ForwardReference() +PyFloatObject = lltype.Ptr(PyFloatObjectStruct) +PyFloatObjectFields = PyObjectFields + \ + (("ob_fval", rffi.DOUBLE),) +cpython_struct("PyFloatObject", PyFloatObjectFields, PyFloatObjectStruct) + +@bootstrap_function +def init_floatobject(space): + "Type description of PyFloatObject" + make_typedescr(space.w_float.layout.typedef, + basestruct=PyFloatObject.TO, + attach=float_attach, + realize=float_realize) + +def float_attach(space, py_obj, w_obj): + """ + Fills a newly allocated PyFloatObject with the given float object. The + value must not be modified. + """ + py_float = rffi.cast(PyFloatObject, py_obj) + py_float.c_ob_fval = space.float_w(w_obj) + +def float_realize(space, obj): + floatval = rffi.cast(lltype.Float, rffi.cast(PyFloatObject, obj).c_ob_fval) + w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) + w_obj = space.allocate_instance(W_FloatObject, w_type) + w_obj.__init__(floatval) + track_reference(space, obj, w_obj) + return w_obj PyFloat_Check, PyFloat_CheckExact = build_type_checkers("Float") diff --git a/pypy/module/cpyext/include/Python.h b/pypy/module/cpyext/include/Python.h --- a/pypy/module/cpyext/include/Python.h +++ b/pypy/module/cpyext/include/Python.h @@ -120,9 +120,11 @@ #include "intobject.h" #include "longobject.h" #include "listobject.h" +#include "longobject.h" #include "unicodeobject.h" #include "compile.h" #include "frameobject.h" +#include "memoryobject.h" #include "eval.h" #include "pymem.h" #include "pycobject.h" diff --git a/pypy/module/cpyext/include/bytesobject.h b/pypy/module/cpyext/include/bytesobject.h --- a/pypy/module/cpyext/include/bytesobject.h +++ b/pypy/module/cpyext/include/bytesobject.h @@ -1,5 +1,4 @@ - -/* String object interface */ +/* A copy of pypy2's PyStringObject */ #ifndef Py_BYTESOBJECT_H #define Py_BYTESOBJECT_H @@ -7,15 +6,61 @@ extern "C" { #endif +#include <stdarg.h> + #define PyBytes_GET_SIZE(op) PyBytes_Size(op) #define PyBytes_AS_STRING(op) PyBytes_AsString(op) +/* +Type PyStringObject represents a character string. An extra zero byte is +reserved at the end to ensure it is zero-terminated, but a size is +present so strings with null bytes in them can be represented. This +is an immutable object type. + +There are functions to create new string objects, to test +an object for string-ness, and to get the +string value. The latter function returns a null pointer +if the object is not of the proper type. +There is a variant that takes an explicit size as well as a +variant that assumes a zero-terminated string. Note that none of the +functions should be applied to nil objects. +*/ + +/* Caching the hash (ob_shash) saves recalculation of a string's hash value. + Interning strings (ob_sstate) tries to ensure that only one string + object with a given value exists, so equality tests can be one pointer + comparison. This is generally restricted to strings that "look like" + Python identifiers, although the intern() builtin can be used to force + interning of any string. + Together, these sped cpython up by up to 20%, and since they are part of the + "public" interface PyPy must reimpliment them. */ + + + typedef struct { - PyObject_HEAD - char* buffer; - Py_ssize_t size; + PyObject_VAR_HEAD + long ob_shash; + int ob_sstate; + char * buffer; /* change the name from cpython so all non-api c access is thwarted */ + + /* Invariants + * (not relevant in PyPy, all stringobjects are backed by a pypy object) + * buffer contains space for 'ob_size+1' elements. + * buffer[ob_size] == 0. + * ob_shash is the hash of the string or -1 if not computed yet. + * ob_sstate != 0 iff the string object is in stringobject.c's + * 'interned' dictionary; in this case the two references + * from 'interned' to this object are *not counted* in ob_refcnt. + */ + } PyBytesObject; +#define SSTATE_NOT_INTERNED 0 +#define SSTATE_INTERNED_MORTAL 1 +#define SSTATE_INTERNED_IMMORTAL 2 +#define PyString_CHECK_INTERNED(op) (((PyStringObject *)(op))->ob_sstate) + + #define PyByteArray_Check(obj) \ PyObject_IsInstance(obj, (PyObject *)&PyByteArray_Type) diff --git a/pypy/module/cpyext/include/complexobject.h b/pypy/module/cpyext/include/complexobject.h --- a/pypy/module/cpyext/include/complexobject.h +++ b/pypy/module/cpyext/include/complexobject.h @@ -6,14 +6,16 @@ extern "C" { #endif -/* fake PyComplexObject so that code that doesn't do direct field access works */ -#define PyComplexObject PyObject - typedef struct Py_complex_t { double real; double imag; } Py_complex; +typedef struct { + PyObject_HEAD + Py_complex cval; +} PyComplexObject; + /* generated function */ PyAPI_FUNC(int) _PyComplex_AsCComplex(PyObject *, Py_complex *); PyAPI_FUNC(PyObject *) _PyComplex_FromCComplex(Py_complex *); diff --git a/pypy/module/cpyext/include/datetime.h b/pypy/module/cpyext/include/datetime.h --- a/pypy/module/cpyext/include/datetime.h +++ b/pypy/module/cpyext/include/datetime.h @@ -24,6 +24,18 @@ PyObject_HEAD } PyDateTime_Delta; +typedef struct { + PyObject_HEAD +} PyDateTime_Date; + +typedef struct { + PyObject_HEAD +} PyDateTime_Time; + +typedef struct { + PyObject_HEAD +} PyDateTime_DateTime; + #ifdef __cplusplus } #endif diff --git a/pypy/module/cpyext/include/descrobject.h b/pypy/module/cpyext/include/descrobject.h --- a/pypy/module/cpyext/include/descrobject.h +++ b/pypy/module/cpyext/include/descrobject.h @@ -12,4 +12,34 @@ } PyGetSetDef; +#define PyDescr_COMMON \ + PyObject_HEAD \ + PyTypeObject *d_type; \ + PyObject *d_name + +typedef struct { + PyDescr_COMMON; +} PyDescrObject; + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; + #endif diff --git a/pypy/module/cpyext/include/floatobject.h b/pypy/module/cpyext/include/floatobject.h --- a/pypy/module/cpyext/include/floatobject.h +++ b/pypy/module/cpyext/include/floatobject.h @@ -3,10 +3,22 @@ #ifndef Py_FLOATOBJECT_H #define Py_FLOATOBJECT_H + +#ifdef _MSC_VER +#include <math.h> +#include <float.h> +#define copysign _copysign +#endif + #ifdef __cplusplus extern "C" { #endif +typedef struct { + PyObject_HEAD + double ob_fval; +} PyFloatObject; + #define PyFloat_STR_PRECISION 12 #ifdef Py_NAN diff --git a/pypy/module/cpyext/include/longobject.h b/pypy/module/cpyext/include/longobject.h --- a/pypy/module/cpyext/include/longobject.h +++ b/pypy/module/cpyext/include/longobject.h @@ -1,12 +1,20 @@ - -/* Int object interface */ - #ifndef Py_LONGOBJECT_H #define Py_LONGOBJECT_H + +#include <stdlib.h> + #ifdef __cplusplus extern "C" { #endif +/* why does cpython redefine these, and even supply an implementation in mystrtoul.c? +PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int); +PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int); +*/ + +#define PyOS_strtoul strtoul +#define PyOS_strtol strtoul + #define PyLong_AS_LONG(op) PyLong_AsLong(op) #ifdef __cplusplus diff --git a/pypy/module/cpyext/include/memoryobject.h b/pypy/module/cpyext/include/memoryobject.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/include/memoryobject.h @@ -0,0 +1,14 @@ +#ifndef Py_MEMORYOBJECT_H +#define Py_MEMORYOBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MEMORYOBJECT_H */ diff --git a/pypy/module/cpyext/include/numpy/README b/pypy/module/cpyext/include/numpy/README new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/include/numpy/README @@ -0,0 +1,8 @@ +headers for the micronumpy multiarray and umath modules, +as used by https://bitbucket.org/pypy/numpy. They are needed by +downstream packages that depend on numpy, like matplotlib, but can +be slightly non-compatible with traditional numpy C-API use cases. + +The trick to including these headers is in get_include, located in +numpy/lib/utils.py. They will be ignored by an upstream build of numpy +since the <site-packages>/numpy/core/include path will be used instead diff --git a/pypy/module/cpyext/include/numpy/__multiarray_api.h b/pypy/module/cpyext/include/numpy/__multiarray_api.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/include/numpy/__multiarray_api.h @@ -0,0 +1,11 @@ + + +typedef struct { + PyObject_HEAD + npy_bool obval; +} PyBoolScalarObject; + +static int import_array(){return 0;}; +static int _import_array(){return 0;}; +static int _import_math(){return 0;}; + diff --git a/pypy/module/cpyext/include/numpy/arrayobject.h b/pypy/module/cpyext/include/numpy/arrayobject.h --- a/pypy/module/cpyext/include/numpy/arrayobject.h +++ b/pypy/module/cpyext/include/numpy/arrayobject.h @@ -1,6 +1,8 @@ -/* NDArray object interface - S. H. Muller, 2013/07/26 */ -/* For testing ndarrayobject only */ +/* NDArray object interface - S. H. Muller, 2013/07/26 + * It will be copied by numpy/core/setup.py by install_data to + * site-packages/numpy/core/includes/numpy +*/ #ifndef Py_NDARRAYOBJECT_H #define Py_NDARRAYOBJECT_H @@ -8,8 +10,14 @@ extern "C" { #endif +#include "pypy_numpy.h" +#include "old_defines.h" #include "npy_common.h" -#include "ndarraytypes.h" +#include "__multiarray_api.h" + +#define NPY_UNUSED(x) x +#define PyArray_MAX(a,b) (((a)>(b))?(a):(b)) +#define PyArray_MIN(a,b) (((a)<(b))?(a):(b)) /* fake PyArrayObject so that code that doesn't do direct field access works */ #define PyArrayObject PyObject @@ -17,20 +25,206 @@ PyAPI_DATA(PyTypeObject) PyArray_Type; -#define PyArray_SimpleNew _PyArray_SimpleNew -#define PyArray_ZEROS _PyArray_ZEROS -#define PyArray_CopyInto _PyArray_CopyInto -#define PyArray_FILLWBYTE _PyArray_FILLWBYTE #define NPY_MAXDIMS 32 -/* functions defined in ndarrayobject.c*/ +#ifndef NDARRAYTYPES_H +typedef struct { + npy_intp *ptr; + int len; +} PyArray_Dims; + +/* data types copied from numpy/ndarraytypes.h + * keep numbers in sync with micronumpy.interp_dtype.DTypeCache + */ +enum NPY_TYPES { NPY_BOOL=0, + NPY_BYTE, NPY_UBYTE, + NPY_SHORT, NPY_USHORT, + NPY_INT, NPY_UINT, + NPY_LONG, NPY_ULONG, + NPY_LONGLONG, NPY_ULONGLONG, + NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, + NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, + NPY_OBJECT=17, + NPY_STRING, NPY_UNICODE, + NPY_VOID, + /* + * New 1.6 types appended, may be integrated + * into the above in 2.0. + */ + NPY_DATETIME, NPY_TIMEDELTA, NPY_HALF, + + NPY_NTYPES, + NPY_NOTYPE, + NPY_CHAR, /* special flag */ + NPY_USERDEF=256, /* leave room for characters */ + + /* The number of types not including the new 1.6 types */ + NPY_NTYPES_ABI_COMPATIBLE=21 +}; + +#define PyTypeNum_ISBOOL(type) ((type) == NPY_BOOL) +#define PyTypeNum_ISINTEGER(type) (((type) >= NPY_BYTE) && \ + ((type) <= NPY_ULONGLONG)) +#define PyTypeNum_ISFLOAT(type) ((((type) >= NPY_FLOAT) && \ + ((type) <= NPY_LONGDOUBLE)) || \ + ((type) == NPY_HALF)) +#define PyTypeNum_ISCOMPLEX(type) (((type) >= NPY_CFLOAT) && \ + ((type) <= NPY_CLONGDOUBLE)) + +#define PyArray_ISBOOL(arr) (PyTypeNum_ISBOOL(PyArray_TYPE(arr))) +#define PyArray_ISINTEGER(arr) (PyTypeNum_ISINTEGER(PyArray_TYPE(arr))) +#define PyArray_ISFLOAT(arr) (PyTypeNum_ISFLOAT(PyArray_TYPE(arr))) +#define PyArray_ISCOMPLEX(arr) (PyTypeNum_ISCOMPLEX(PyArray_TYPE(arr))) + + +/* flags */ +#define NPY_ARRAY_C_CONTIGUOUS 0x0001 +#define NPY_ARRAY_F_CONTIGUOUS 0x0002 +#define NPY_ARRAY_OWNDATA 0x0004 +#define NPY_ARRAY_FORCECAST 0x0010 +#define NPY_ARRAY_ENSURECOPY 0x0020 +#define NPY_ARRAY_ENSUREARRAY 0x0040 +#define NPY_ARRAY_ELEMENTSTRIDES 0x0080 +#define NPY_ARRAY_ALIGNED 0x0100 +#define NPY_ARRAY_NOTSWAPPED 0x0200 +#define NPY_ARRAY_WRITEABLE 0x0400 +#define NPY_ARRAY_UPDATEIFCOPY 0x1000 + +#define NPY_ARRAY_BEHAVED (NPY_ARRAY_ALIGNED | \ + NPY_ARRAY_WRITEABLE) +#define NPY_ARRAY_BEHAVED_NS (NPY_ARRAY_ALIGNED | \ + NPY_ARRAY_WRITEABLE | \ + NPY_ARRAY_NOTSWAPPED) +#define NPY_ARRAY_CARRAY (NPY_ARRAY_C_CONTIGUOUS | \ + NPY_ARRAY_BEHAVED) +#define NPY_ARRAY_CARRAY_RO (NPY_ARRAY_C_CONTIGUOUS | \ + NPY_ARRAY_ALIGNED) +#define NPY_ARRAY_FARRAY (NPY_ARRAY_F_CONTIGUOUS | \ + NPY_ARRAY_BEHAVED) +#define NPY_ARRAY_FARRAY_RO (NPY_ARRAY_F_CONTIGUOUS | \ + NPY_ARRAY_ALIGNED) +#define NPY_ARRAY_DEFAULT (NPY_ARRAY_CARRAY) +#define NPY_ARRAY_IN_ARRAY (NPY_ARRAY_CARRAY_RO) +#define NPY_ARRAY_OUT_ARRAY (NPY_ARRAY_CARRAY) +#define NPY_ARRAY_INOUT_ARRAY (NPY_ARRAY_CARRAY | \ + NPY_ARRAY_UPDATEIFCOPY) +#define NPY_ARRAY_IN_FARRAY (NPY_ARRAY_FARRAY_RO) +#define NPY_ARRAY_OUT_FARRAY (NPY_ARRAY_FARRAY) +#define NPY_ARRAY_INOUT_FARRAY (NPY_ARRAY_FARRAY | \ + NPY_ARRAY_UPDATEIFCOPY) + +#define NPY_ARRAY_UPDATE_ALL (NPY_ARRAY_C_CONTIGUOUS | \ + NPY_ARRAY_F_CONTIGUOUS | \ + NPY_ARRAY_ALIGNED) + +#define NPY_FARRAY NPY_ARRAY_FARRAY +#define NPY_CARRAY NPY_ARRAY_CARRAY + +#define PyArray_CHKFLAGS(m, flags) (PyArray_FLAGS(m) & (flags)) + +#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS(m, NPY_ARRAY_WRITEABLE) +#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS(m, NPY_ARRAY_ALIGNED) + +#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS) + +#define PyArray_FLAGSWAP(m, flags) (PyArray_CHKFLAGS(m, flags) && \ + PyArray_ISNOTSWAPPED(m)) + +#define PyArray_ISCARRAY(m) PyArray_FLAGSWAP(m, NPY_ARRAY_CARRAY) +#define PyArray_ISCARRAY_RO(m) PyArray_FLAGSWAP(m, NPY_ARRAY_CARRAY_RO) +#define PyArray_ISFARRAY(m) PyArray_FLAGSWAP(m, NPY_ARRAY_FARRAY) +#define PyArray_ISFARRAY_RO(m) PyArray_FLAGSWAP(m, NPY_ARRAY_FARRAY_RO) +#define PyArray_ISBEHAVED(m) PyArray_FLAGSWAP(m, NPY_ARRAY_BEHAVED) +#define PyArray_ISBEHAVED_RO(m) PyArray_FLAGSWAP(m, NPY_ARRAY_ALIGNED) + +#define PyArray_ISONESEGMENT(arr) (1) +#define PyArray_ISNOTSWAPPED(arr) (1) +#define PyArray_ISBYTESWAPPED(arr) (0) + +#endif + +#define NPY_INT8 NPY_BYTE +#define NPY_UINT8 NPY_UBYTE +#define NPY_INT16 NPY_SHORT +#define NPY_UINT16 NPY_USHORT +#define NPY_INT32 NPY_INT +#define NPY_UINT32 NPY_UINT +#define NPY_INT64 NPY_LONG +#define NPY_UINT64 NPY_ULONG +#define NPY_FLOAT32 NPY_FLOAT +#define NPY_FLOAT64 NPY_DOUBLE +#define NPY_COMPLEX32 NPY_CFLOAT +#define NPY_COMPLEX64 NPY_CDOUBLE + + +/* functions */ +#ifndef PyArray_NDIM + +#define PyArray_Check _PyArray_Check +#define PyArray_CheckExact _PyArray_CheckExact +#define PyArray_FLAGS _PyArray_FLAGS + +#define PyArray_NDIM _PyArray_NDIM +#define PyArray_DIM _PyArray_DIM +#define PyArray_STRIDE _PyArray_STRIDE +#define PyArray_SIZE _PyArray_SIZE +#define PyArray_ITEMSIZE _PyArray_ITEMSIZE +#define PyArray_NBYTES _PyArray_NBYTES +#define PyArray_TYPE _PyArray_TYPE +#define PyArray_DATA _PyArray_DATA + +#define PyArray_Size PyArray_SIZE +#define PyArray_BYTES(arr) ((char *)PyArray_DATA(arr)) + +#define PyArray_FromAny _PyArray_FromAny +#define PyArray_FromObject _PyArray_FromObject +#define PyArray_ContiguousFromObject PyArray_FromObject +#define PyArray_ContiguousFromAny PyArray_FromObject + +#define PyArray_FROMANY(obj, typenum, min, max, requirements) (obj) +#define PyArray_FROM_OTF(obj, typenum, requirements) \ + PyArray_FromObject(obj, typenum, 0, 0) + +#define PyArray_New _PyArray_New +#define PyArray_SimpleNew _PyArray_SimpleNew +#define PyArray_SimpleNewFromData _PyArray_SimpleNewFromData +#define PyArray_SimpleNewFromDataOwning _PyArray_SimpleNewFromDataOwning + +#define PyArray_EMPTY(nd, dims, type_num, fortran) \ + PyArray_SimpleNew(nd, dims, type_num) PyAPI_FUNC(void) _PyArray_FILLWBYTE(PyObject* obj, int val); PyAPI_FUNC(PyObject *) _PyArray_ZEROS(int nd, npy_intp* dims, int type_num, int fortran); -PyAPI_FUNC(int) _PyArray_CopyInto(PyArrayObject* dest, PyArrayObject* src); +#define PyArray_FILLWBYTE _PyArray_FILLWBYTE +#define PyArray_ZEROS _PyArray_ZEROS +#define PyArray_Resize(self, newshape, refcheck, fortran) (NULL) + +/* Don't use these in loops! */ + +#define PyArray_GETPTR1(obj, i) ((void *)(PyArray_BYTES(obj) + \ + (i)*PyArray_STRIDE(obj,0))) + +#define PyArray_GETPTR2(obj, i, j) ((void *)(PyArray_BYTES(obj) + \ + (i)*PyArray_STRIDE(obj,0) + \ + (j)*PyArray_STRIDE(obj,1))) + +#define PyArray_GETPTR3(obj, i, j, k) ((void *)(PyArray_BYTES(obj) + \ + (i)*PyArray_STRIDE(obj,0) + \ + (j)*PyArray_STRIDE(obj,1) + \ + (k)*PyArray_STRIDE(obj,2))) + +#define PyArray_GETPTR4(obj, i, j, k, l) ((void *)(PyArray_BYTES(obj) + \ + (i)*PyArray_STRIDE(obj,0) + \ + (j)*PyArray_STRIDE(obj,1) + \ + (k)*PyArray_STRIDE(obj,2) + \ + (l)*PyArray_STRIDE(obj,3))) + +#endif #ifdef __cplusplus } diff --git a/pypy/module/cpyext/include/numpy/ndarraytypes.h b/pypy/module/cpyext/include/numpy/ndarraytypes.h --- a/pypy/module/cpyext/include/numpy/ndarraytypes.h +++ b/pypy/module/cpyext/include/numpy/ndarraytypes.h @@ -1,9 +1,69 @@ #ifndef NDARRAYTYPES_H #define NDARRAYTYPES_H -/* For testing ndarrayobject only */ +#include "numpy/npy_common.h" +//#include "npy_endian.h" +//#include "npy_cpu.h" +//#include "utils.h" -#include "numpy/npy_common.h" +//for pypy - numpy has lots of typedefs +//for pypy - make life easier, less backward support +#define NPY_1_8_API_VERSION 0x00000008 +#define NPY_NO_DEPRECATED_API NPY_1_8_API_VERSION +#undef NPY_1_8_API_VERSION + +#define NPY_ENABLE_SEPARATE_COMPILATION 1 +#define NPY_VISIBILITY_HIDDEN + +#ifdef NPY_ENABLE_SEPARATE_COMPILATION + #define NPY_NO_EXPORT NPY_VISIBILITY_HIDDEN +#else + #define NPY_NO_EXPORT static +#endif + +/* Only use thread if configured in config and python supports it */ +#if defined WITH_THREAD && !NPY_NO_SMP + #define NPY_ALLOW_THREADS 1 +#else + #define NPY_ALLOW_THREADS 0 +#endif + + + +/* + * There are several places in the code where an array of dimensions + * is allocated statically. This is the size of that static + * allocation. + * + * The array creation itself could have arbitrary dimensions but all + * the places where static allocation is used would need to be changed + * to dynamic (including inside of several structures) + */ + +#define NPY_MAXDIMS 32 +#define NPY_MAXARGS 32 + +/* Used for Converter Functions "O&" code in ParseTuple */ +#define NPY_FAIL 0 +#define NPY_SUCCEED 1 + +/* + * Binary compatibility version number. This number is increased + * whenever the C-API is changed such that binary compatibility is + * broken, i.e. whenever a recompile of extension modules is needed. + */ +#define NPY_VERSION NPY_ABI_VERSION + +/* + * Minor API version. This number is increased whenever a change is + * made to the C-API -- whether it breaks binary compatibility or not. + * Some changes, such as adding a function pointer to the end of the + * function table, can be made without breaking binary compatibility. + * In this case, only the NPY_FEATURE_VERSION (*not* NPY_VERSION) + * would be increased. Whenever binary compatibility is broken, both + * NPY_VERSION and NPY_FEATURE_VERSION should be increased. + */ +#define NPY_FEATURE_VERSION NPY_API_VERSION enum NPY_TYPES { NPY_BOOL=0, NPY_BYTE, NPY_UBYTE, @@ -31,6 +91,18 @@ NPY_NTYPES_ABI_COMPATIBLE=21 }; +/* basetype array priority */ +#define NPY_PRIORITY 0.0 + +/* default subtype priority */ +#define NPY_SUBTYPE_PRIORITY 1.0 + +/* default scalar priority */ +#define NPY_SCALAR_PRIORITY -1000000.0 + +/* How many floating point types are there (excluding half) */ +#define NPY_NUM_FLOATTYPE 3 + /* * These characters correspond to the array type and the struct * module @@ -85,6 +157,27 @@ }; typedef enum { + NPY_QUICKSORT=0, + NPY_HEAPSORT=1, + NPY_MERGESORT=2 +} NPY_SORTKIND; +#define NPY_NSORTS (NPY_MERGESORT + 1) + + +typedef enum { + NPY_INTROSELECT=0, +} NPY_SELECTKIND; +#define NPY_NSELECTS (NPY_INTROSELECT + 1) + + +typedef enum { + NPY_SEARCHLEFT=0, + NPY_SEARCHRIGHT=1 +} NPY_SEARCHSIDE; +#define NPY_NSEARCHSIDES (NPY_SEARCHRIGHT + 1) + + +typedef enum { NPY_NOSCALAR=-1, NPY_BOOL_SCALAR, NPY_INTPOS_SCALAR, @@ -93,6 +186,7 @@ NPY_COMPLEX_SCALAR, NPY_OBJECT_SCALAR } NPY_SCALARKIND; +#define NPY_NSCALARKINDS (NPY_OBJECT_SCALAR + 1) /* For specifying array memory layout or iteration order */ typedef enum { @@ -106,6 +200,729 @@ NPY_KEEPORDER=2 } NPY_ORDER; +/* For specifying allowed casting in operations which support it */ +typedef enum { + /* Only allow identical types */ + NPY_NO_CASTING=0, + /* Allow identical and byte swapped types */ + NPY_EQUIV_CASTING=1, + /* Only allow safe casts */ + NPY_SAFE_CASTING=2, + /* Allow safe casts or casts within the same kind */ + NPY_SAME_KIND_CASTING=3, + /* Allow any casts */ + NPY_UNSAFE_CASTING=4, + + /* + * Temporary internal definition only, will be removed in upcoming + * release, see below + * */ + NPY_INTERNAL_UNSAFE_CASTING_BUT_WARN_UNLESS_SAME_KIND = 100, +} NPY_CASTING; + +typedef enum { + NPY_CLIP=0, + NPY_WRAP=1, + NPY_RAISE=2 +} NPY_CLIPMODE; + +/* The special not-a-time (NaT) value */ +#define NPY_DATETIME_NAT NPY_MIN_INT64 + +/* + * Upper bound on the length of a DATETIME ISO 8601 string + * YEAR: 21 (64-bit year) + * MONTH: 3 + * DAY: 3 + * HOURS: 3 + * MINUTES: 3 + * SECONDS: 3 + * ATTOSECONDS: 1 + 3*6 + * TIMEZONE: 5 + * NULL TERMINATOR: 1 + */ +#define NPY_DATETIME_MAX_ISO8601_STRLEN (21+3*5+1+3*6+6+1) + +typedef enum { + NPY_FR_Y = 0, /* Years */ + NPY_FR_M = 1, /* Months */ + NPY_FR_W = 2, /* Weeks */ + /* Gap where 1.6 NPY_FR_B (value 3) was */ + NPY_FR_D = 4, /* Days */ + NPY_FR_h = 5, /* hours */ + NPY_FR_m = 6, /* minutes */ + NPY_FR_s = 7, /* seconds */ + NPY_FR_ms = 8, /* milliseconds */ + NPY_FR_us = 9, /* microseconds */ + NPY_FR_ns = 10,/* nanoseconds */ + NPY_FR_ps = 11,/* picoseconds */ + NPY_FR_fs = 12,/* femtoseconds */ + NPY_FR_as = 13,/* attoseconds */ + NPY_FR_GENERIC = 14 /* Generic, unbound units, can convert to anything */ +} NPY_DATETIMEUNIT; + +/* + * NOTE: With the NPY_FR_B gap for 1.6 ABI compatibility, NPY_DATETIME_NUMUNITS + * is technically one more than the actual number of units. + */ +#define NPY_DATETIME_NUMUNITS (NPY_FR_GENERIC + 1) +#define NPY_DATETIME_DEFAULTUNIT NPY_FR_GENERIC + +/* + * Business day conventions for mapping invalid business + * days to valid business days. + */ +typedef enum { + /* Go forward in time to the following business day. */ + NPY_BUSDAY_FORWARD, + NPY_BUSDAY_FOLLOWING = NPY_BUSDAY_FORWARD, + /* Go backward in time to the preceding business day. */ + NPY_BUSDAY_BACKWARD, + NPY_BUSDAY_PRECEDING = NPY_BUSDAY_BACKWARD, + /* + * Go forward in time to the following business day, unless it + * crosses a month boundary, in which case go backward + */ + NPY_BUSDAY_MODIFIEDFOLLOWING, + /* + * Go backward in time to the preceding business day, unless it + * crosses a month boundary, in which case go forward. + */ + NPY_BUSDAY_MODIFIEDPRECEDING, + /* Produce a NaT for non-business days. */ + NPY_BUSDAY_NAT, + /* Raise an exception for non-business days. */ + NPY_BUSDAY_RAISE +} NPY_BUSDAY_ROLL; + +/************************************************************ + * NumPy Auxiliary Data for inner loops, sort functions, etc. + ************************************************************/ + +/* + * When creating an auxiliary data struct, this should always appear + * as the first member, like this: + * + * typedef struct { + * NpyAuxData base; + * double constant; + * } constant_multiplier_aux_data; + */ +typedef struct NpyAuxData_tag NpyAuxData; + +/* Function pointers for freeing or cloning auxiliary data */ +typedef void (NpyAuxData_FreeFunc) (NpyAuxData *); +typedef NpyAuxData *(NpyAuxData_CloneFunc) (NpyAuxData *); + +struct NpyAuxData_tag { + NpyAuxData_FreeFunc *free; + NpyAuxData_CloneFunc *clone; + /* To allow for a bit of expansion without breaking the ABI */ + void *reserved[2]; +}; + +/* Macros to use for freeing and cloning auxiliary data */ +#define NPY_AUXDATA_FREE(auxdata) \ + do { \ + if ((auxdata) != NULL) { \ + (auxdata)->free(auxdata); \ + } \ + } while(0) +#define NPY_AUXDATA_CLONE(auxdata) \ + ((auxdata)->clone(auxdata)) + +#define NPY_ERR(str) fprintf(stderr, #str); fflush(stderr); +#define NPY_ERR2(str) fprintf(stderr, str); fflush(stderr); + +#define NPY_STRINGIFY(x) #x +#define NPY_TOSTRING(x) NPY_STRINGIFY(x) + + /* + * Macros to define how array, and dimension/strides data is + * allocated. + */ + + /* Data buffer - PyDataMem_NEW/FREE/RENEW are in multiarraymodule.c */ + +#define NPY_USE_PYMEM 1 + +#if NPY_USE_PYMEM == 1 +#define PyArray_malloc PyMem_Malloc +#define PyArray_free PyMem_Free +#define PyArray_realloc PyMem_Realloc +#else +#define PyArray_malloc malloc +#define PyArray_free free +#define PyArray_realloc realloc +#endif + +/* Dimensions and strides */ +#define PyDimMem_NEW(size) \ + ((npy_intp *)PyArray_malloc(size*sizeof(npy_intp))) + +#define PyDimMem_FREE(ptr) PyArray_free(ptr) + +#define PyDimMem_RENEW(ptr,size) \ + ((npy_intp *)PyArray_realloc(ptr,size*sizeof(npy_intp))) + +/* forward declaration */ +struct _PyArray_Descr; + +/* These must deal with unaligned and swapped data if necessary */ +typedef PyObject * (PyArray_GetItemFunc) (void *, void *); +typedef int (PyArray_SetItemFunc)(PyObject *, void *, void *); + +typedef void (PyArray_CopySwapNFunc)(void *, npy_intp, void *, npy_intp, + npy_intp, int, void *); + +typedef void (PyArray_CopySwapFunc)(void *, void *, int, void *); +typedef npy_bool (PyArray_NonzeroFunc)(void *, void *); + + +/* + * These assume aligned and notswapped data -- a buffer will be used + * before or contiguous data will be obtained + */ + +typedef int (PyArray_CompareFunc)(const void *, const void *, void *); +typedef int (PyArray_ArgFunc)(void*, npy_intp, npy_intp*, void *); + +typedef void (PyArray_DotFunc)(void *, npy_intp, void *, npy_intp, void *, + npy_intp, void *); + +typedef void (PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, + void *); + +/* + * XXX the ignore argument should be removed next time the API version + * is bumped. It used to be the separator. + */ +typedef int (PyArray_ScanFunc)(FILE *fp, void *dptr, + char *ignore, struct _PyArray_Descr *); +typedef int (PyArray_FromStrFunc)(char *s, void *dptr, char **endptr, + struct _PyArray_Descr *); + +typedef int (PyArray_FillFunc)(void *, npy_intp, void *); + +typedef int (PyArray_SortFunc)(void *, npy_intp, void *); +typedef int (PyArray_ArgSortFunc)(void *, npy_intp *, npy_intp, void *); +typedef int (PyArray_PartitionFunc)(void *, npy_intp, npy_intp, + npy_intp *, npy_intp *, + void *); +typedef int (PyArray_ArgPartitionFunc)(void *, npy_intp *, npy_intp, npy_intp, + npy_intp *, npy_intp *, + void *); + +typedef int (PyArray_FillWithScalarFunc)(void *, npy_intp, void *, void *); + +typedef int (PyArray_ScalarKindFunc)(void *); + +typedef void (PyArray_FastClipFunc)(void *in, npy_intp n_in, void *min, + void *max, void *out); +typedef void (PyArray_FastPutmaskFunc)(void *in, void *mask, npy_intp n_in, + void *values, npy_intp nv); +typedef int (PyArray_FastTakeFunc)(void *dest, void *src, npy_intp *indarray, + npy_intp nindarray, npy_intp n_outer, + npy_intp m_middle, npy_intp nelem, + NPY_CLIPMODE clipmode); + +typedef struct { + npy_intp *ptr; + int len; +} PyArray_Dims; + +typedef struct { + /* + * Functions to cast to most other standard types + * Can have some NULL entries. The types + * DATETIME, TIMEDELTA, and HALF go into the castdict + * even though they are built-in. + */ + PyArray_VectorUnaryFunc *cast[NPY_NTYPES_ABI_COMPATIBLE]; + + /* The next four functions *cannot* be NULL */ + + /* + * Functions to get and set items with standard Python types + * -- not array scalars + */ + PyArray_GetItemFunc *getitem; + PyArray_SetItemFunc *setitem; + + /* + * Copy and/or swap data. Memory areas may not overlap + * Use memmove first if they might + */ + PyArray_CopySwapNFunc *copyswapn; + PyArray_CopySwapFunc *copyswap; + + /* + * Function to compare items + * Can be NULL + */ + PyArray_CompareFunc *compare; + + /* + * Function to select largest + * Can be NULL + */ + PyArray_ArgFunc *argmax; + + /* + * Function to compute dot product + * Can be NULL + */ + PyArray_DotFunc *dotfunc; + + /* + * Function to scan an ASCII file and + * place a single value plus possible separator + * Can be NULL + */ + PyArray_ScanFunc *scanfunc; + + /* + * Function to read a single value from a string + * and adjust the pointer; Can be NULL + */ + PyArray_FromStrFunc *fromstr; + + /* + * Function to determine if data is zero or not + * If NULL a default version is + * used at Registration time. + */ + PyArray_NonzeroFunc *nonzero; + + /* + * Used for arange. + * Can be NULL. + */ + PyArray_FillFunc *fill; + + /* + * Function to fill arrays with scalar values + * Can be NULL + */ + PyArray_FillWithScalarFunc *fillwithscalar; + + /* + * Sorting functions + * Can be NULL + */ + PyArray_SortFunc *sort[NPY_NSORTS]; + PyArray_ArgSortFunc *argsort[NPY_NSORTS]; + + /* + * Dictionary of additional casting functions + * PyArray_VectorUnaryFuncs + * which can be populated to support casting + * to other registered types. Can be NULL + */ + PyObject *castdict; + + /* + * Functions useful for generalizing + * the casting rules. + * Can be NULL; + */ + PyArray_ScalarKindFunc *scalarkind; + int **cancastscalarkindto; + int *cancastto; + + PyArray_FastClipFunc *fastclip; + PyArray_FastPutmaskFunc *fastputmask; + PyArray_FastTakeFunc *fasttake; + + /* + * Function to select smallest + * Can be NULL + */ + PyArray_ArgFunc *argmin; + +} PyArray_ArrFuncs; + +/* The item must be reference counted when it is inserted or extracted. */ +#define NPY_ITEM_REFCOUNT 0x01 +/* Same as needing REFCOUNT */ +#define NPY_ITEM_HASOBJECT 0x01 +/* Convert to list for pickling */ +#define NPY_LIST_PICKLE 0x02 +/* The item is a POINTER */ +#define NPY_ITEM_IS_POINTER 0x04 +/* memory needs to be initialized for this data-type */ +#define NPY_NEEDS_INIT 0x08 +/* operations need Python C-API so don't give-up thread. */ +#define NPY_NEEDS_PYAPI 0x10 +/* Use f.getitem when extracting elements of this data-type */ +#define NPY_USE_GETITEM 0x20 +/* Use f.setitem when setting creating 0-d array from this data-type.*/ +#define NPY_USE_SETITEM 0x40 +/* A sticky flag specifically for structured arrays */ +#define NPY_ALIGNED_STRUCT 0x80 + +/* + *These are inherited for global data-type if any data-types in the + * field have them + */ +#define NPY_FROM_FIELDS (NPY_NEEDS_INIT | NPY_LIST_PICKLE | \ + NPY_ITEM_REFCOUNT | NPY_NEEDS_PYAPI) + +#define NPY_OBJECT_DTYPE_FLAGS (NPY_LIST_PICKLE | NPY_USE_GETITEM | \ + NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT | \ + NPY_NEEDS_INIT | NPY_NEEDS_PYAPI) + +#define PyDataType_FLAGCHK(dtype, flag) \ + (((dtype)->flags & (flag)) == (flag)) + +#define PyDataType_REFCHK(dtype) \ + PyDataType_FLAGCHK(dtype, NPY_ITEM_REFCOUNT) + +typedef struct _PyArray_Descr { + PyObject_HEAD _______________________________________________ pypy-commit mailing list [email protected] https://mail.python.org/mailman/listinfo/pypy-commit
