Author: Ronan Lamy <ronan.l...@gmail.com> Branch: indexing Changeset: r78583:d579a53d3316 Date: 2015-07-17 16:12 +0100 http://bitbucket.org/pypy/pypy/changeset/d579a53d3316/
Log: hg merge default 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 @@ -37,3 +37,9 @@ .. branch: unicode-dtype Partial implementation of unicode dtype and unicode scalars. + +.. branch: dtypes-compatability + +Improve compatibility with numpy dtypes; handle offsets to create unions, +fix str() and repr(), allow specifying itemsize, metadata and titles, add flags, +allow subclassing dtype diff --git a/pypy/module/_vmprof/src/getpc.h b/pypy/module/_vmprof/src/getpc.h --- a/pypy/module/_vmprof/src/getpc.h +++ b/pypy/module/_vmprof/src/getpc.h @@ -132,7 +132,7 @@ } }; -inline void* GetPC(ucontext_t *signal_ucontext) { +void* GetPC(ucontext_t *signal_ucontext) { // See comment above struct CallUnrollInfo. Only try instruction // flow matching if both eip and esp looks reasonable. const int eip = signal_ucontext->uc_mcontext.gregs[REG_EIP]; @@ -168,7 +168,7 @@ typedef int ucontext_t; #endif -inline void* GetPC(ucontext_t *signal_ucontext) { +void* GetPC(ucontext_t *signal_ucontext) { RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n"); return NULL; } @@ -178,7 +178,7 @@ // the right value for your system, and add it to the list in // configure.ac (or set it manually in your config.h). #else -inline void* GetPC(ucontext_t *signal_ucontext) { +void* GetPC(ucontext_t *signal_ucontext) { return (void*)signal_ucontext->PC_FROM_UCONTEXT; // defined in config.h } diff --git a/pypy/module/_vmprof/src/vmprof.c b/pypy/module/_vmprof/src/vmprof.c --- a/pypy/module/_vmprof/src/vmprof.c +++ b/pypy/module/_vmprof/src/vmprof.c @@ -262,13 +262,31 @@ int marker = MARKER_TRAILER; write(profile_file, &marker, 1); +#ifdef __linux__ // copy /proc/PID/maps to the end of the profile file sprintf(buf, "/proc/%d/maps", getpid()); - src = fopen(buf, "r"); + src = fopen(buf, "r"); + if (!src) { + vmprof_error = "error opening proc maps"; + return -1; + } while ((size = fread(buf, 1, BUFSIZ, src))) { write(profile_file, buf, size); } fclose(src); +#else + // freebsd and mac + sprintf(buf, "procstat -v %d", getpid()); + src = popen(buf, "r"); + if (!src) { + vmprof_error = "error calling procstat"; + return -1; + } + while ((size = fread(buf, 1, BUFSIZ, src))) { + write(profile_file, buf, size); + } + pclose(src); +#endif close(profile_file); return 0; } diff --git a/pypy/module/micronumpy/appbridge.py b/pypy/module/micronumpy/appbridge.py --- a/pypy/module/micronumpy/appbridge.py +++ b/pypy/module/micronumpy/appbridge.py @@ -8,6 +8,7 @@ w__commastring = None w_array_repr = None w_array_str = None + w__usefields = None def __init__(self, space): pass diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py --- a/pypy/module/micronumpy/boxes.py +++ b/pypy/module/micronumpy/boxes.py @@ -563,7 +563,7 @@ elif space.isinstance_w(w_item, space.w_int): indx = space.int_w(w_item) try: - item = self.dtype.names[indx] + item = self.dtype.names[indx][0] except IndexError: if indx < 0: indx += len(self.dtype.names) @@ -596,7 +596,7 @@ try: ofs, dtype = self.dtype.fields[item] except KeyError: - raise oefmt(space.w_IndexError, "222only integers, slices (`:`), " + raise oefmt(space.w_IndexError, "only integers, slices (`:`), " "ellipsis (`...`), numpy.newaxis (`None`) and integer or " "boolean arrays are valid indices") dtype.store(self.arr, self.ofs, ofs, 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 @@ -47,6 +47,9 @@ def lookup(self, name): return self.getdictvalue(self, name) + def getname(self, space): + return self.name + class FakeSpace(ObjSpace): w_ValueError = W_TypeObject("ValueError") w_TypeError = W_TypeObject("TypeError") diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -624,15 +624,17 @@ self.impl = impl self.readonly = readonly - def getitem(self, item): - return raw_storage_getitem(lltype.Char, self.impl.storage, item) + def getitem(self, index): + return raw_storage_getitem(lltype.Char, self.impl.storage, + index + self.impl.start) - def setitem(self, item, v): - raw_storage_setitem(self.impl.storage, item, + def setitem(self, index, v): + raw_storage_setitem(self.impl.storage, index + self.impl.start, rffi.cast(lltype.Char, v)) def getlength(self): - return self.impl.size + return self.impl.size - self.impl.start def get_raw_address(self): - return self.impl.storage + from rpython.rtyper.lltypesystem import rffi + return rffi.ptradd(self.impl.storage, self.impl.start) diff --git a/pypy/module/micronumpy/constants.py b/pypy/module/micronumpy/constants.py --- a/pypy/module/micronumpy/constants.py +++ b/pypy/module/micronumpy/constants.py @@ -92,6 +92,21 @@ ARRAY_ELEMENTSTRIDES = 0x0080 # strides are units of the dtype element size ARRAY_NOTSWAPPED = 0x0200 #native byte order +#dtype flags +ITEM_REFCOUNT = 0x01 +ITEM_HASOBJECT = 0x01 +LIST_PICKLE = 0x02 +ITEM_IS_POINTER = 0x04 +NEEDS_INIT = 0x08 +NEEDS_PYAPI = 0x10 +USE_GETITEM = 0x20 +USE_SETITEM = 0x40 +ALIGNED_STRUCT = 0x80 +FROM_FIELDS = NEEDS_INIT | LIST_PICKLE | ITEM_REFCOUNT | NEEDS_PYAPI +OBJECT_DTYPE_FLAGS = (LIST_PICKLE | USE_GETITEM | ITEM_IS_POINTER | + ITEM_REFCOUNT | NEEDS_INIT | NEEDS_PYAPI) + + LITTLE = '<' BIG = '>' NATIVE = '=' diff --git a/pypy/module/micronumpy/descriptor.py b/pypy/module/micronumpy/descriptor.py --- a/pypy/module/micronumpy/descriptor.py +++ b/pypy/module/micronumpy/descriptor.py @@ -54,11 +54,11 @@ class W_Dtype(W_Root): _immutable_fields_ = [ "itemtype?", "w_box_type", "byteorder?", "names?", "fields?", - "elsize?", "alignment?", "shape?", "subdtype?", "base?"] + "elsize?", "alignment?", "shape?", "subdtype?", "base?", "flags?"] @enforceargs(byteorder=SomeChar()) def __init__(self, itemtype, w_box_type, byteorder=NPY.NATIVE, names=[], - fields={}, elsize=None, shape=[], subdtype=None): + fields={}, elsize=-1, shape=[], subdtype=None): self.itemtype = itemtype self.w_box_type = w_box_type if itemtype.get_element_size() == 1 or isinstance(itemtype, types.ObjectType): @@ -66,16 +66,21 @@ self.byteorder = byteorder self.names = names self.fields = fields - if elsize is None: + if elsize < 0: elsize = itemtype.get_element_size() self.elsize = elsize - self.alignment = itemtype.alignment self.shape = shape self.subdtype = subdtype + self.flags = 0 + self.metadata = None + if isinstance(itemtype, types.ObjectType): + self.flags = NPY.OBJECT_DTYPE_FLAGS if not subdtype: self.base = self + self.alignment = itemtype.get_element_size() else: self.base = subdtype.base + self.alignment = subdtype.itemtype.get_element_size() @property def num(self): @@ -172,51 +177,149 @@ return dtype def get_name(self): - name = self.w_box_type.name - if name.startswith('numpy.'): - name = name[6:] + name = self.w_box_type.getname(self.itemtype.space) if name.endswith('_'): name = name[:-1] return name - def descr_get_name(self, space): - name = self.get_name() + def descr_get_name(self, space, quote=False): + if quote: + name = "'" + self.get_name() + "'" + else: + name = self.get_name() if self.is_flexible() and self.elsize != 0: return space.wrap(name + str(self.elsize * 8)) return space.wrap(name) - def descr_get_str(self, space): + def descr_get_str(self, space, ignore='|', simple=True): + if not simple and self.fields and len(self.fields) > 0: + return self.descr_get_descr(space) + total = 0 + for s in self.shape: + total += s + if not simple and total > 0: + return space.newtuple( + [space.wrap(self.subdtype.get_str(ignore='')), + space.newtuple([space.wrap(s) for s in self.shape]), + ]) + return space.wrap(self.get_str(ignore=ignore)) + + def get_str(self, ignore='|'): basic = self.kind endian = self.byteorder size = self.elsize if endian == NPY.NATIVE: endian = NPY.NATBYTE + elif endian == NPY.IGNORE: + endian = ignore if self.num == NPY.UNICODE: size >>= 2 - return space.wrap("%s%s%s" % (endian, basic, size)) + return "%s%s%s" % (endian, basic, size) - def descr_get_descr(self, space): + def descr_get_descr(self, space, style='descr', force_dict=False): + simple = False + if style == 'descr': + simple = True if not self.is_record(): return space.newlist([space.newtuple([space.wrap(""), - self.descr_get_str(space)])]) + self.descr_get_str(space, simple=simple)])]) + elif (self.alignment > 1 and not style.startswith('descr')) or force_dict: + # we need to force a sorting order for the keys, + # so return a string instead of a dict. Also, numpy formats + # the lists without spaces between elements, so we cannot simply + # do str(names) + names = ["'names':["] + formats = ["'formats':["] + offsets = ["'offsets':["] + titles = ["'titles':["] + use_titles = False + show_offsets = False + offsets_n = [] + total = 0 + for name, title in self.names: + offset, subdtype = self.fields[name] + if subdtype.is_record(): + substr = [space.str_w(space.str(subdtype.descr_get_descr( + space, style='descr_subdtype'))), ","] + elif subdtype.subdtype is not None: + substr = ["(", space.str_w(space.str( + subdtype.subdtype.descr_get_descr(space, style='descr_subdtype'))), + ', ', + space.str_w(space.repr(space.newtuple([space.wrap(s) for s in subdtype.shape]))), + "),"] + else: + substr = ["'", subdtype.get_str(ignore=''), "',"] + formats += substr + offsets += [str(offset), ','] + names += ["'", name, "',"] + titles += ["'", str(title), "',"] + if title is not None: + use_titles = True + if total != offset: + show_offsets = True + total += subdtype.elsize + # make sure offsets_n is sorted + i = 0 + for i in range(len(offsets_n)): + if offset < offsets_n[i]: + break + offsets_n.insert(i, offset) + total = 0 + for i in range(len(offsets_n)): + if offsets_n[i] != self.alignment * i: + show_offsets = True + if use_titles and not show_offsets: + return self.descr_get_descr(space, style='descr') + # replace the last , with a ] + formats[-1] = formats[-1][:-1] + ']' + offsets[-1] = offsets[-1][:-1] + ']' + names[-1] = names[-1][:-1] + ']' + titles[-1] = titles[-1][:-1] + ']' + if self.alignment < 2 or style.endswith('subdtype'): + suffix = "}" + elif style == 'str': + suffix = ", 'aligned':True}" + elif style == 'substr': + suffix = '}' + else: + suffix = "}, align=True" + s_as_list = ['{'] + names + [', '] + formats + [', '] + offsets + [', '] + if use_titles: + s_as_list += titles + [', '] + + s_as_list += ["'itemsize':", str(self.elsize), suffix] + return space.wrap(''.join(s_as_list)) else: descr = [] - for name in self.names: - subdtype = self.fields[name][1] - subdescr = [space.wrap(name)] + total = 0 + for name, title in self.names: + offset, subdtype = self.fields[name] + show_offsets = False + if total != offset and len(subdtype.shape) < 1: + # whoops, need to use other format + return self.descr_get_descr(space, style=style + '_subdtype', force_dict=True) + total += subdtype.elsize + ignore = '|' + if title: + subdescr = [space.newtuple([space.wrap(title), space.wrap(name)])] + ignore = '' + else: + subdescr = [space.wrap(name)] if subdtype.is_record(): - subdescr.append(subdtype.descr_get_descr(space)) + subdescr.append(subdtype.descr_get_descr(space, style)) elif subdtype.subdtype is not None: - subdescr.append(subdtype.subdtype.descr_get_str(space)) + subdescr.append(subdtype.subdtype.descr_get_str(space, simple=False)) else: - subdescr.append(subdtype.descr_get_str(space)) + subdescr.append(subdtype.descr_get_str(space, ignore=ignore, simple=False)) if subdtype.shape != []: subdescr.append(subdtype.descr_get_shape(space)) descr.append(space.newtuple(subdescr[:])) + if self.alignment >= 0 and not style.endswith('subdtype'): + return space.wrap(space.str_w(space.repr(space.newlist(descr))) + ', align=True') return space.newlist(descr) def descr_get_hasobject(self, space): - return space.w_False + return space.wrap(self.is_object()) def descr_get_isbuiltin(self, space): if self.fields is None: @@ -238,19 +341,28 @@ def descr_get_shape(self, space): return space.newtuple([space.wrap(dim) for dim in self.shape]) + def descr_get_flags(self, space): + return space.wrap(self.flags) + def descr_get_fields(self, space): if not self.fields: return space.w_None w_fields = space.newdict() - for name, (offset, subdtype) in self.fields.iteritems(): - space.setitem(w_fields, space.wrap(name), + for name, title in self.names: + offset, subdtype = self.fields[name] + if title is not None: + w_nt = space.newtuple([space.wrap(name), space.wrap(title)]) + space.setitem(w_fields, w_nt, + space.newtuple([subdtype, space.wrap(offset)])) + else: + space.setitem(w_fields, space.wrap(name), space.newtuple([subdtype, space.wrap(offset)])) return w_fields def descr_get_names(self, space): if not self.fields: return space.w_None - return space.newtuple([space.wrap(name) for name in self.names]) + return space.newtuple([space.wrap(name[0]) for name in self.names]) def descr_set_names(self, space, w_names): if not self.fields: @@ -262,23 +374,43 @@ "with a sequence of length %d", len(self.names)) names = [] - for w_name in space.fixedview(w_names): + names_w = space.fixedview(w_names) + for i in range(len(names_w)): + w_name = names_w[i] + title = self.names[i][1] if not space.isinstance_w(w_name, space.w_str): raise oefmt(space.w_ValueError, "item #%d of names is of type %T and not string", len(names), w_name) - names.append(space.str_w(w_name)) + names.append((space.str_w(w_name), title)) fields = {} for i in range(len(self.names)): - if names[i] in fields: + if names[i][0] in fields: raise oefmt(space.w_ValueError, "Duplicate field names given.") - fields[names[i]] = self.fields[self.names[i]] + fields[names[i][0]] = self.fields[self.names[i][0]] + if self.names[i][1] is not None: + fields[self.names[i][1]] = self.fields[self.names[i][0]] self.fields = fields self.names = names def descr_del_names(self, space): - raise OperationError(space.w_AttributeError, space.wrap( - "Cannot delete dtype names attribute")) + raise oefmt(space.w_AttributeError, + "Cannot delete dtype names attribute") + + def descr_get_metadata(self, space): + if self.metadata is None: + return space.w_None + return self.metadata + + def descr_set_metadata(self, space, w_metadata): + if w_metadata is None: + return + if not space.isinstance_w(w_metadata, space.w_dict): + raise oefmt(space.w_TypeError, "argument 4 must be dict, not str") + self.metadata = w_metadata + + def descr_del_metadata(self, space): + self.metadata = None def eq(self, space, w_other): w_other = space.call_function(space.gettypefor(W_Dtype), w_other) @@ -331,7 +463,8 @@ y = intmask((1000003 * y) ^ self.alignment) return intmask((1000003 * x) ^ y) if self.fields: - for name, (offset, subdtype) in self.fields.iteritems(): + for name in self.fields.keys(): + offset, subdtype = self.fields[name] assert isinstance(subdtype, W_Dtype) y = intmask(1000003 * (0x345678 ^ compute_hash(name))) y = intmask(1000003 * (y ^ compute_hash(offset))) @@ -349,7 +482,7 @@ def descr_str(self, space): if self.fields: - return space.str(self.descr_get_descr(space)) + return space.str(self.descr_get_descr(space, style='str')) elif self.subdtype is not None: return space.str(space.newtuple([ self.subdtype.descr_get_str(space), @@ -362,7 +495,7 @@ def descr_repr(self, space): if self.fields: - r = self.descr_get_descr(space) + r = self.descr_get_descr(space, style='repr') elif self.subdtype is not None: r = space.newtuple([self.subdtype.descr_get_str(space), self.descr_get_shape(space)]) @@ -375,9 +508,11 @@ size = self.elsize if self.num == NPY.UNICODE: size >>= 2 - r = space.wrap(byteorder + self.char + str(size)) + r = space.wrap("'" + byteorder + self.char + str(size) + "'") else: - r = self.descr_get_name(space) + r = self.descr_get_name(space, quote=True) + if space.isinstance_w(r, space.w_str): + return space.wrap("dtype(%s)" % space.str_w(r)) return space.wrap("dtype(%s)" % space.str_w(space.repr(r))) def descr_getitem(self, space, w_item): @@ -389,7 +524,7 @@ elif space.isinstance_w(w_item, space.w_int): indx = space.int_w(w_item) try: - item = self.names[indx] + item,title = self.names[indx] except IndexError: raise oefmt(space.w_IndexError, "Field index %d out of range.", indx) @@ -436,14 +571,17 @@ values = self.descr_get_fields(space) if self.is_flexible(): w_size = space.wrap(self.elsize) - alignment = space.wrap(self.alignment) + if self.alignment > 2: + w_alignment = space.wrap(self.alignment) + else: + w_alignment = space.wrap(1) else: w_size = space.wrap(-1) - alignment = space.wrap(-1) - flags = space.wrap(0) + w_alignment = space.wrap(-1) + w_flags = space.wrap(self.flags) data = space.newtuple([version, space.wrap(endian), subdescr, - names, values, w_size, alignment, flags]) + names, values, w_size, w_alignment, w_flags]) return space.newtuple([w_class, builder_args, data]) def descr_setstate(self, space, w_data): @@ -465,6 +603,9 @@ w_fields = space.getitem(w_data, space.wrap(4)) size = space.int_w(space.getitem(w_data, space.wrap(5))) alignment = space.int_w(space.getitem(w_data, space.wrap(6))) + if alignment < 2: + alignment = -1 + flags = space.int_w(space.getitem(w_data, space.wrap(7))) if (w_names == space.w_None) != (w_fields == space.w_None): raise oefmt(space.w_ValueError, "inconsistent fields and names in Numpy dtype unpickling") @@ -492,20 +633,21 @@ self.names = [] self.fields = {} for w_name in space.fixedview(w_names): + # XXX what happens if there is a title in the pickled dtype? name = space.str_w(w_name) value = space.getitem(w_fields, w_name) dtype = space.getitem(value, space.wrap(0)) + offset = space.int_w(space.getitem(value, space.wrap(1))) + self.names.append((name, None)) assert isinstance(dtype, W_Dtype) - offset = space.int_w(space.getitem(value, space.wrap(1))) - - self.names.append(name) self.fields[name] = offset, dtype self.itemtype = types.RecordType(space) if self.is_flexible(): self.elsize = size self.alignment = alignment + self.flags = flags @unwrap_spec(new_order=str) def descr_newbyteorder(self, space, new_order=NPY.SWAP): @@ -526,17 +668,24 @@ @specialize.arg(2) -def dtype_from_list(space, w_lst, simple, align=False): +def dtype_from_list(space, w_lst, simple, alignment, offsets=None, itemsize=0): lst_w = space.listview(w_lst) fields = {} - offset = 0 - names = [] - maxalign = 0 + use_supplied_offsets = True + if offsets is None: + use_supplied_offsets = False + offsets = [0] * len(lst_w) + maxalign = alignment + fldnames = [''] * len(lst_w) + subdtypes = [None] * len(lst_w) + titles = [None] * len(lst_w) + total = 0 for i in range(len(lst_w)): w_elem = lst_w[i] if simple: - subdtype = descr__new__(space, space.gettypefor(W_Dtype), w_elem) - fldname = 'f%d' % i + subdtype = make_new_dtype(space, space.gettypefor(W_Dtype), w_elem, + maxalign) + fldnames[i] = 'f%d' % i else: w_shape = space.newtuple([]) if space.len_w(w_elem) == 3: @@ -545,52 +694,213 @@ w_shape = space.newtuple([w_shape]) else: w_fldname, w_flddesc = space.fixedview(w_elem, 2) - subdtype = descr__new__(space, space.gettypefor(W_Dtype), w_flddesc, w_shape=w_shape) - fldname = space.str_w(w_fldname) - if fldname == '': - fldname = 'f%d' % i - if fldname in fields: + subdtype = make_new_dtype(space, space.gettypefor(W_Dtype), + w_flddesc, maxalign, w_shape=w_shape) + if space.isinstance_w(w_fldname, space.w_tuple): + fldlist = space.listview(w_fldname) + fldnames[i] = space.str_w(fldlist[0]) + if space.is_w(fldlist[1], space.w_None): + titles[i] = None + else: + titles[i] = space.str_w(fldlist[1]) + if len(fldlist) != 2: + raise oefmt(space.w_TypeError, "data type not understood") + elif space.isinstance_w(w_fldname, space.w_str): + fldnames[i] = space.str_w(w_fldname) + else: + raise oefmt(space.w_TypeError, "data type not understood") + if fldnames[i] == '': + fldnames[i] = 'f%d' % i + assert isinstance(subdtype, W_Dtype) + if alignment >= 0: + maxalign = max(subdtype.alignment, maxalign) + delta = subdtype.alignment + # Set offset to the next power-of-two above delta + delta = (delta + maxalign -1) & (-maxalign) + if not use_supplied_offsets: + if delta > offsets[i]: + for j in range(i): + offsets[j+1] = delta + offsets[j] + if i + 1 < len(offsets) and offsets[i + 1] == 0: + offsets[i + 1] = offsets[i] + max(delta, subdtype.elsize) + elif not use_supplied_offsets: + if i + 1 < len(offsets) and offsets[i + 1] == 0: + offsets[i+1] = offsets[i] + subdtype.elsize + subdtypes[i] = subdtype + if use_supplied_offsets: + sz = subdtype.elsize + else: + sz = max(maxalign, subdtype.elsize) + if offsets[i] + sz > total: + total = offsets[i] + sz + # padding? + if alignment >= 0 and total % maxalign: + total = total // maxalign * maxalign + maxalign + names = [] + for i in range(len(subdtypes)): + subdtype = subdtypes[i] + assert isinstance(subdtype, W_Dtype) + if alignment >=0 and subdtype.is_record(): + subdtype.alignment = maxalign + if fldnames[i] in fields: + raise oefmt(space.w_ValueError, "two fields with the same name") + if maxalign > 1 and offsets[i] % subdtype.alignment: + raise oefmt(space.w_ValueError, "offset %d for NumPy dtype with " + "fields is not divisible by the field alignment %d " + "with align=True", offsets[i], maxalign) + fields[fldnames[i]] = offsets[i], subdtype + if titles[i] is not None: + if titles[i] in fields: raise oefmt(space.w_ValueError, "two fields with the same name") - assert isinstance(subdtype, W_Dtype) - fields[fldname] = (offset, subdtype) - offset += subdtype.elsize - maxalign = max(subdtype.elsize, maxalign) - names.append(fldname) + fields[titles[i]] = offsets[i], subdtype + names.append((fldnames[i], titles[i])) + if itemsize > 1: + if total > itemsize: + raise oefmt(space.w_ValueError, + "NumPy dtype descriptor requires %d bytes, cannot" + " override to smaller itemsize of %d", total, itemsize) + if alignment >= 0 and itemsize % maxalign: + raise oefmt(space.w_ValueError, + "NumPy dtype descriptor requires alignment of %d bytes, " + "which is not divisible into the specified itemsize %d", + maxalign, itemsize) + total = itemsize + retval = W_Dtype(types.RecordType(space), space.gettypefor(boxes.W_VoidBox), + names=names, fields=fields, elsize=total) + if alignment >=0: + retval.alignment = maxalign + else: + retval.alignment = -1 + retval.flags |= NPY.NEEDS_PYAPI + return retval + +def _get_val_or_none(space, w_dict, key): + w_key = space.wrap(key) + try: + w_val = space.getitem(w_dict, w_key) + except OperationError as e: + if e.match(space, space.w_KeyError): + return None + else: + raise + return w_val + +def _get_list_or_none(space, w_dict, key): + w_val = _get_val_or_none(space, w_dict, key) + if w_val is None: + return None + if space.isinstance_w(w_val, space.w_set): + raise oefmt(space.w_TypeError, "'set' object does not support indexing") + return space.listview(w_val) + +def _usefields(space, w_dict, align): + # Only for testing, a shortened version of the real _usefields + allfields = [] + for fname in w_dict.iterkeys().iterator: + obj = _get_list_or_none(space, w_dict, fname) + num = space.int_w(obj[1]) + if align: + alignment = 0 + else: + alignment = -1 + format = dtype_from_spec(space, obj[0], alignment=alignment) + if len(obj) > 2: + title = space.wrap(obj[2]) + else: + title = space.w_None + allfields.append((space.wrap(fname), format, num, title)) + allfields.sort(key=lambda x: x[2]) + names = [space.newtuple([x[0], x[3]]) for x in allfields] + formats = [x[1] for x in allfields] + offsets = [x[2] for x in allfields] + aslist = [] if align: - # Set offset to the next power-of-two above offset - offset = (offset + maxalign -1) & (-maxalign) - return W_Dtype(types.RecordType(space), space.gettypefor(boxes.W_VoidBox), - names=names, fields=fields, elsize=offset) + alignment = 0 + else: + alignment = -1 + for i in range(len(names)): + aslist.append(space.newtuple([space.wrap(names[i]), space.wrap(formats[i])])) + return dtype_from_list(space, space.newlist(aslist), False, alignment, offsets=offsets) + +def dtype_from_dict(space, w_dict, alignment): + from pypy.objspace.std.dictmultiobject import W_DictMultiObject + assert isinstance(w_dict, W_DictMultiObject) + names_w = _get_list_or_none(space, w_dict, 'names') + formats_w = _get_list_or_none(space, w_dict, 'formats') + offsets_w = _get_list_or_none(space, w_dict, 'offsets') + titles_w = _get_list_or_none(space, w_dict, 'titles') + metadata_w = _get_val_or_none(space, w_dict, 'metadata') + aligned_w = _get_val_or_none(space, w_dict, 'align') + itemsize_w = _get_val_or_none(space, w_dict, 'itemsize') + if names_w is None or formats_w is None: + if we_are_translated(): + return get_appbridge_cache(space).call_method(space, + 'numpy.core._internal', '_usefields', Arguments(space, + [w_dict, space.wrap(alignment >= 0)])) + else: + return _usefields(space, w_dict, alignment >= 0) + n = len(names_w) + if (n != len(formats_w) or + (offsets_w is not None and n != len(offsets_w)) or + (titles_w is not None and n != len(titles_w))): + raise oefmt(space.w_ValueError, "'names', 'formats', 'offsets', and " + "'titles' dicct entries must have the same length") + if aligned_w is not None: + if space.isinstance_w(aligned_w, space.w_bool) and space.is_true(aligned_w): + if alignment < 0: + alignment = 0 + else: + raise oefmt(space.w_ValueError, + "NumPy dtype descriptor includes 'aligned' entry, " + "but its value is neither True nor False"); + if offsets_w is None: + offsets = None + else: + offsets = [space.int_w(i) for i in offsets_w] + if titles_w is not None: + _names_w = [] + for i in range(min(len(names_w), len(titles_w))): + _names_w.append(space.newtuple([names_w[i], titles_w[i]])) + names_w = _names_w + aslist = [] + if itemsize_w is None: + itemsize = 0 + else: + itemsize = space.int_w(itemsize_w) + for i in range(min(len(names_w), len(formats_w))): + aslist.append(space.newtuple([names_w[i], formats_w[i]])) + retval = dtype_from_list(space, space.newlist(aslist), False, alignment, + offsets=offsets, itemsize=itemsize) + if metadata_w is not None: + retval.descr_set_metadata(space, metadata_w) + retval.flags |= NPY.NEEDS_PYAPI + return retval - -def dtype_from_dict(space, w_dict): - raise OperationError(space.w_NotImplementedError, space.wrap( - "dtype from dict")) - - -def dtype_from_spec(space, w_spec): +def dtype_from_spec(space, w_spec, alignment): if we_are_translated(): w_lst = get_appbridge_cache(space).call_method(space, 'numpy.core._internal', '_commastring', Arguments(space, [w_spec])) else: - # testing, handle manually - if space.eq_w(w_spec, space.wrap('u4,u4,u4')): - w_lst = space.newlist([space.wrap('u4')]*3) - if space.eq_w(w_spec, space.wrap('u4,u4,u4')): - w_lst = space.newlist([space.wrap('u4')]*3) - else: - raise oefmt(space.w_RuntimeError, - "cannot parse w_spec") + # handle only simple cases for testing + if space.isinstance_w(w_spec, space.w_str): + spec = [s.strip() for s in space.str_w(w_spec).split(',')] + w_lst = space.newlist([space.wrap(s) for s in spec]) + elif space.isinstance_w(w_spec, space.w_list): + w_lst = w_spec if not space.isinstance_w(w_lst, space.w_list) or space.len_w(w_lst) < 1: raise oefmt(space.w_RuntimeError, "_commastring is not returning a list with len >= 1") if space.len_w(w_lst) == 1: return descr__new__(space, space.gettypefor(W_Dtype), - space.getitem(w_lst, space.wrap(0))) + space.getitem(w_lst, space.wrap(0)), align=alignment>0) else: - return dtype_from_list(space, w_lst, True) - + try: + return dtype_from_list(space, w_lst, True, alignment) + except OperationError as e: + if e.match(space, space.w_TypeError): + return dtype_from_list(space, w_lst, False, alignment) + raise def _check_for_commastring(s): if s[0] in string.digits or s[0] in '<>=|' and s[1] in string.digits: @@ -608,30 +918,83 @@ sqbracket -= 1 return False -@unwrap_spec(align=bool) -def descr__new__(space, w_subtype, w_dtype, align=False, w_copy=None, w_shape=None): - # align and w_copy are necessary for pickling +def _set_metadata_and_copy(space, w_metadata, dtype, copy=False): cache = get_dtype_cache(space) + assert isinstance(dtype, W_Dtype) + if copy or (dtype in cache.builtin_dtypes and w_metadata is not None): + dtype = W_Dtype(dtype.itemtype, dtype.w_box_type, dtype.byteorder) + if w_metadata is not None: + dtype.descr_set_metadata(space, w_metadata) + return dtype - if w_shape is not None and (space.isinstance_w(w_shape, space.w_int) or - space.len_w(w_shape) > 0): - subdtype = descr__new__(space, w_subtype, w_dtype, align, w_copy) +def _get_shape(space, w_shape): + if w_shape is None: + return None + if space.isinstance_w(w_shape, space.w_int): + dim = space.int_w(w_shape) + if dim == 1: + return None + return [dim] + shape_w = space.fixedview(w_shape) + if len(shape_w) < 1: + return None + elif len(shape_w) == 1 and space.isinstance_w(shape_w[0], space.w_tuple): + # (base_dtype, new_dtype) dtype spectification + return None + shape = [] + for w_dim in shape_w: + try: + dim = space.int_w(w_dim) + except OperationError as e: + if e.match(space, space.w_OverflowError): + raise oefmt(space.w_ValueError, "invalid shape in fixed-type tuple.") + else: + raise + if dim > int(0x7fffffff): + raise oefmt(space.w_ValueError, "invalid shape in fixed-type tuple: " + "dimension does not fit into a C int.") + elif dim < 0: + raise oefmt(space.w_ValueError, "invalid shape in fixed-type tuple: " + "dimension smaller than zero.") + shape.append(dim) + return shape + +@unwrap_spec(align=bool, copy=bool) +def descr__new__(space, w_subtype, w_dtype, align=False, copy=False, + w_shape=None, w_metadata=None): + if align: + alignment = 0 + else: + alignment = -1 + return make_new_dtype(space, w_subtype, w_dtype, alignment, copy=copy, + w_shape=w_shape, w_metadata=w_metadata) + +def make_new_dtype(space, w_subtype, w_dtype, alignment, copy=False, w_shape=None, w_metadata=None): + cache = get_dtype_cache(space) + shape = _get_shape(space, w_shape) + if shape is not None: + subdtype = make_new_dtype(space, w_subtype, w_dtype, alignment, copy, w_metadata=w_metadata) assert isinstance(subdtype, W_Dtype) - size = 1 - if space.isinstance_w(w_shape, space.w_int): - w_shape = space.newtuple([w_shape]) - shape = [] - for w_dim in space.fixedview(w_shape): - dim = space.int_w(w_dim) - shape.append(dim) - size *= dim - if size == 1: - return subdtype + size = support.product(shape) size *= subdtype.elsize - return W_Dtype(types.VoidType(space), - space.gettypefor(boxes.W_VoidBox), - shape=shape, subdtype=subdtype, elsize=size) - + if size > int(0x7fffffff): + raise oefmt(space.w_ValueError, "invalid shape in fixed-type tuple: " + "dtype size in bytes must fit into a C int.") + + return _set_metadata_and_copy(space, w_metadata, + W_Dtype(types.VoidType(space), space.gettypefor(boxes.W_VoidBox), + shape=shape, subdtype=subdtype, elsize=size)) + elif w_shape is not None and not space.isinstance_w(w_shape, space.w_int): + spec = space.listview(w_shape) + if len(spec) > 0: + # this is (base_dtype, new_dtype) so just make it a union by setting both + # parts' offset to 0 + try: + dtype1 = make_new_dtype(space, w_subtype, w_shape, alignment) + except: + raise + raise oefmt(space.w_NotImplementedError, + "(base_dtype, new_dtype) dtype spectification discouraged, not implemented") if space.is_none(w_dtype): return cache.w_float64dtype if space.isinstance_w(w_dtype, w_subtype): @@ -641,7 +1004,8 @@ if space.isinstance_w(w_dtype, space.w_str): name = space.str_w(w_dtype) if _check_for_commastring(name): - return dtype_from_spec(space, w_dtype) + return _set_metadata_and_copy(space, w_metadata, + dtype_from_spec(space, w_dtype, alignment)) cname = name[1:] if name[0] == NPY.OPPBYTE else name try: dtype = cache.dtypes_by_name[cname] @@ -655,26 +1019,34 @@ return variable_dtype(space, name) raise oefmt(space.w_TypeError, 'data type "%s" not understood', name) elif space.isinstance_w(w_dtype, space.w_list): - return dtype_from_list(space, w_dtype, False, align=align) + return _set_metadata_and_copy( space, w_metadata, + dtype_from_list(space, w_dtype, False, alignment), copy) elif space.isinstance_w(w_dtype, space.w_tuple): w_dtype0 = space.getitem(w_dtype, space.wrap(0)) w_dtype1 = space.getitem(w_dtype, space.wrap(1)) - subdtype = descr__new__(space, w_subtype, w_dtype0, align, w_copy) + subdtype = make_new_dtype(space, w_subtype, w_dtype0, alignment, copy) assert isinstance(subdtype, W_Dtype) if subdtype.elsize == 0: name = "%s%d" % (subdtype.kind, space.int_w(w_dtype1)) - return descr__new__(space, w_subtype, space.wrap(name), align, w_copy) - return descr__new__(space, w_subtype, w_dtype0, align, w_copy, w_shape=w_dtype1) + retval = make_new_dtype(space, w_subtype, space.wrap(name), alignment, copy) + else: + retval = make_new_dtype(space, w_subtype, w_dtype0, alignment, copy, w_shape=w_dtype1) + return _set_metadata_and_copy(space, w_metadata, retval, copy) elif space.isinstance_w(w_dtype, space.w_dict): - return dtype_from_dict(space, w_dtype) + return _set_metadata_and_copy(space, w_metadata, + dtype_from_dict(space, w_dtype, alignment), copy) for dtype in cache.builtin_dtypes: if dtype.num in cache.alternate_constructors and \ w_dtype in cache.alternate_constructors[dtype.num]: - return dtype + return _set_metadata_and_copy(space, w_metadata, dtype, copy) if w_dtype is dtype.w_box_type: - return dtype + return _set_metadata_and_copy(space, w_metadata, dtype, copy) + if space.isinstance_w(w_dtype, space.w_type) and \ + space.is_true(space.issubtype(w_dtype, dtype.w_box_type)): + return _set_metadata_and_copy( space, w_metadata, + W_Dtype(dtype.itemtype, w_dtype, elsize=0), copy) if space.isinstance_w(w_dtype, space.w_type): - return cache.w_objectdtype + return _set_metadata_and_copy(space, w_metadata, cache.w_objectdtype, copy) raise oefmt(space.w_TypeError, "data type not understood") @@ -702,6 +1074,11 @@ names = GetSetProperty(W_Dtype.descr_get_names, W_Dtype.descr_set_names, W_Dtype.descr_del_names), + metadata = GetSetProperty(W_Dtype.descr_get_metadata, + #W_Dtype.descr_set_metadata, + #W_Dtype.descr_del_metadata, + ), + flags = GetSetProperty(W_Dtype.descr_get_flags), __eq__ = interp2app(W_Dtype.descr_eq), __ne__ = interp2app(W_Dtype.descr_ne), diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -258,7 +258,7 @@ if field not in dtype.fields: raise oefmt(space.w_ValueError, "field named %s not found", field) arr = self.implementation - ofs, subdtype = arr.dtype.fields[field] + ofs, subdtype = arr.dtype.fields[field][:2] # ofs only changes start # create a view of the original array by extending # the shape, strides, backstrides of the array diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py --- a/pypy/module/micronumpy/test/test_dtypes.py +++ b/pypy/module/micronumpy/test/test_dtypes.py @@ -92,6 +92,7 @@ assert d == np.dtype('i8') assert d.shape == () d = np.dtype((np.int64, 1,)) + assert d.shape == () assert d == np.dtype('i8') assert d.shape == () d = np.dtype((np.int64, 4)) @@ -111,6 +112,7 @@ assert "int8" == dtype("int8") raises(TypeError, lambda: dtype("int8") == 3) assert dtype(bool) == bool + assert dtype('f8') != dtype(('f8', (1,))) def test_dtype_cmp(self): from numpy import dtype @@ -342,10 +344,10 @@ raises(TypeError, type, "Foo", (dtype,), {}) def test_can_subclass(self): - import numpy - class xyz(numpy.void): + import numpy as np + class xyz(np.void): pass - assert True + assert np.dtype(xyz).name == 'xyz' def test_index(self): import numpy as np @@ -413,7 +415,7 @@ assert loads(dumps(a.dtype)) == a.dtype assert np.dtype('bool').__reduce__() == (dtype, ('b1', 0, 1), (3, '|', None, None, None, -1, -1, 0)) assert np.dtype('|V16').__reduce__() == (dtype, ('V16', 0, 1), (3, '|', None, None, None, 16, 1, 0)) - assert np.dtype(('<f8', 2)).__reduce__() == (dtype, ('V16', 0, 1), (3, '|', (dtype('float64'), (2,)), None, None, 16, 1, 0)) + assert np.dtype(('<f8', 2)).__reduce__() == (dtype, ('V16', 0, 1), (3, '|', (dtype('float64'), (2,)), None, None, 16, 8, 0)) def test_newbyteorder(self): import numpy as np @@ -480,13 +482,21 @@ class O(object): pass for o in [object, O]: - print np.dtype(o).byteorder if self.ptr_size == 4: assert np.dtype(o).str == '|O4' elif self.ptr_size == 8: assert np.dtype(o).str == '|O8' else: assert False,'self._ptr_size unknown' + # Issue gh-2798 + if '__pypy__' in sys.builtin_module_names: + a = np.array(['a'], dtype="O") + raises(NotImplementedError, a.astype, ("O", [("name", "O")])) + skip("(base_dtype, new_dtype) dtype specification discouraged") + a = np.array(['a'], dtype="O").astype(("O", [("name", "O")])) + assert a[0] == 'a' + assert a == 'a' + assert a['name'].dtype == a.dtype class AppTestTypes(BaseAppTestDtypes): def test_abstract_types(self): @@ -686,16 +696,8 @@ numpy.integer, numpy.number, numpy.generic, object] import sys - if '__pypy__' not in sys.builtin_module_names: - # These tests pass "by chance" on numpy, things that are larger than - # platform long (i.e. a python int), don't get put in a normal box, - # instead they become an object array containing a long, we don't have - # yet, so these can't pass. - assert numpy.uint64(9223372036854775808) == 9223372036854775808 - assert numpy.uint64(18446744073709551615) == 18446744073709551615 - else: - raises(OverflowError, numpy.int64, 9223372036854775808) - raises(OverflowError, numpy.int64, 18446744073709551615) + raises(OverflowError, numpy.int64, 9223372036854775808) + raises(OverflowError, numpy.int64, 18446744073709551615) raises(OverflowError, numpy.uint64, 18446744073709551616) assert numpy.uint64((2<<63) - 1) == (2<<63) - 1 @@ -1136,10 +1138,7 @@ import sys d = {'names': ['r','g','b','a'], 'formats': [np.uint8, np.uint8, np.uint8, np.uint8]} - if '__pypy__' not in sys.builtin_module_names: - dt = np.dtype(d) - else: - raises(NotImplementedError, np.dtype, d) + dt = np.dtype(d) def test_create_subarrays(self): from numpy import dtype @@ -1272,7 +1271,7 @@ ('x', 'y', 'z', 'value'), {'y': (dtype('int32'), 4), 'x': (dtype('int32'), 0), 'z': (dtype('int32'), 8), 'value': (dtype('float64'), 12), - }, 20, 1, 0)) + }, 20, 1, 16)) new_d = loads(dumps(d)) @@ -1291,6 +1290,182 @@ assert new_d.itemsize == d.itemsize == 76 + def test_shape_invalid(self): + import numpy as np + # Check that the shape is valid. + max_int = 2 ** (8 * 4 - 1) + max_intp = 2 ** (8 * np.dtype('intp').itemsize - 1) - 1 + # Too large values (the datatype is part of this) + raises(ValueError, np.dtype, [('a', 'f4', max_int // 4 + 1)]) + raises(ValueError, np.dtype, [('a', 'f4', max_int + 1)]) + raises(ValueError, np.dtype, [('a', 'f4', (max_int, 2))]) + # Takes a different code path (fails earlier: + raises(ValueError, np.dtype, [('a', 'f4', max_intp + 1)]) + # Negative values + raises(ValueError, np.dtype, [('a', 'f4', -1)]) + raises(ValueError, np.dtype, [('a', 'f4', (-1, -1))]) + + def test_aligned_size(self): + import numpy as np + # Check that structured dtypes get padded to an aligned size + dt = np.dtype('i4, i1', align=True) + assert dt.itemsize == 8 + dt = np.dtype([('f0', 'i4'), ('f1', 'i1')], align=True) + assert dt.itemsize == 8 + dt = np.dtype({'names':['f0', 'f1'], + 'formats':['i4', 'u1'], + 'offsets':[0, 4]}, align=True) + assert dt.itemsize == 8 + dt = np.dtype({'f0': ('i4', 0), 'f1':('u1', 4)}, align=True) + assert dt.itemsize == 8 + assert dt.alignment == 4 + assert str(dt) == "{'names':['f0','f1'], 'formats':['<i4','u1'], 'offsets':[0,4], 'itemsize':8, 'aligned':True}" + dt = np.dtype([('f1', 'u1'), ('f0', 'i4')], align=True) + assert str(dt) == "{'names':['f1','f0'], 'formats':['u1','<i4'], 'offsets':[0,4], 'itemsize':8, 'aligned':True}" + # Nesting should preserve that alignment + dt1 = np.dtype([('f0', 'i4'), + ('f1', [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')]), + ('f2', 'i1')], align=True) + assert dt1.alignment == 4 + assert dt1['f1'].itemsize == 12 + assert dt1.itemsize == 20 + dt2 = np.dtype({'names':['f0', 'f1', 'f2'], + 'formats':['i4', + [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], + 'i1'], + 'offsets':[0, 4, 16]}, align=True) + assert dt2.itemsize == 20 + dt3 = np.dtype({'f0': ('i4', 0), + 'f1': ([('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], 4), + 'f2': ('i1', 16)}, align=True) + assert dt3.itemsize == 20 + assert dt1 == dt2 + answer = "{'names':['f0','f1','f2'], " + \ + "'formats':['<i4',{'names':['f1','f2','f3'], " + \ + "'formats':['i1','<i4','i1'], " + \ + "'offsets':[0,4,8], 'itemsize':12}," + \ + "'i1'], " + \ + "'offsets':[0,4,16], 'itemsize':20, 'aligned':True}" + assert str(dt3) == answer + assert dt2 == dt3 + # Nesting should preserve packing + dt1 = np.dtype([('f0', 'i4'), + ('f1', [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')]), + ('f2', 'i1')], align=False) + assert dt1.itemsize == 11 + dt2 = np.dtype({'names':['f0', 'f1', 'f2'], + 'formats':['i4', + [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], + 'i1'], + 'offsets':[0, 4, 10]}, align=False) + assert dt2.itemsize == 11 + dt3 = np.dtype({'f0': ('i4', 0), + 'f1': ([('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], 4), + 'f2': ('i1', 10)}, align=False) + assert dt3.itemsize == 11 + assert dt1 == dt2 + assert dt2 == dt3 + + def test_bad_param(self): + import numpy as np + # Can't give a size that's too small + raises(ValueError, np.dtype, + {'names':['f0', 'f1'], + 'formats':['i4', 'i1'], + 'offsets':[0, 4], + 'itemsize':4}) + # If alignment is enabled, the alignment (4) must divide the itemsize + raises(ValueError, np.dtype, + {'names':['f0', 'f1'], + 'formats':['i4', 'i1'], + 'offsets':[0, 4], + 'itemsize':9}, align=True) + # If alignment is enabled, the individual fields must be aligned + raises(ValueError, np.dtype, + {'names':['f0', 'f1'], + 'formats':['i1', 'f4'], + 'offsets':[0, 2]}, align=True) + dt = np.dtype(np.double) + attr = ["subdtype", "descr", "str", "name", "base", "shape", + "isbuiltin", "isnative", "isalignedstruct", "fields", + "metadata", "hasobject"] + for s in attr: + raises(AttributeError, delattr, dt, s) + + raises(TypeError, np.dtype, + dict(names=set(['A', 'B']), formats=['f8', 'i4'])) + raises(TypeError, np.dtype, + dict(names=['A', 'B'], formats=set(['f8', 'i4']))) + + def test_complex_dtype_repr(self): + import numpy as np + dt = np.dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), + ('rtile', '>f4', (64, 36))], (3,)), + ('bottom', [('bleft', ('>f4', (8, 64)), (1,)), + ('bright', '>f4', (8, 36))])]) + assert repr(dt) == ( + "dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), " + "('rtile', '>f4', (64, 36))], (3,)), " + "('bottom', [('bleft', ('>f4', (8, 64)), (1,)), " + "('bright', '>f4', (8, 36))])])") + + # If the sticky aligned flag is set to True, it makes the + # str() function use a dict representation with an 'aligned' flag + dt = np.dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), + ('rtile', '>f4', (64, 36))], + (3,)), + ('bottom', [('bleft', ('>f4', (8, 64)), (1,)), + ('bright', '>f4', (8, 36))])], + align=True) + assert str(dt) == ( + "{'names':['top','bottom'], " + "'formats':[([('tiles', ('>f4', (64, 64)), (1,)), " + "('rtile', '>f4', (64, 36))], (3,))," + "[('bleft', ('>f4', (8, 64)), (1,)), " + "('bright', '>f4', (8, 36))]], " + "'offsets':[0,76800], " + "'itemsize':80000, " + "'aligned':True}") + + dt = np.dtype({'names': ['r', 'g', 'b'], 'formats': ['u1', 'u1', 'u1'], + 'offsets': [0, 1, 2], + 'titles': ['Red pixel', 'Green pixel', 'Blue pixel']}, + align=True) + assert repr(dt) == ( + "dtype([(('Red pixel', 'r'), 'u1'), " + "(('Green pixel', 'g'), 'u1'), " + "(('Blue pixel', 'b'), 'u1')], align=True)") + + dt = np.dtype({'names': ['rgba', 'r', 'g', 'b'], + 'formats': ['<u4', 'u1', 'u1', 'u1'], + 'offsets': [0, 0, 1, 2], + 'titles': ['Color', 'Red pixel', + 'Green pixel', 'Blue pixel']}, align=True) + assert repr(dt) == ( + "dtype({'names':['rgba','r','g','b']," + " 'formats':['<u4','u1','u1','u1']," + " 'offsets':[0,0,1,2]," + " 'titles':['Color','Red pixel'," + "'Green pixel','Blue pixel']," + " 'itemsize':4}, align=True)") + + dt = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'], + 'offsets': [0, 2], + 'titles': ['Red pixel', 'Blue pixel'], + 'itemsize': 4}) + assert repr(dt) == ( + "dtype({'names':['r','b'], " + "'formats':['u1','u1'], " + "'offsets':[0,2], " + "'titles':['Red pixel','Blue pixel'], " + "'itemsize':4})") + if 'datetime64' not in dir(np): + skip('datetime dtype not available') + dt = np.dtype([('a', '<M8[D]'), ('b', '<m8[us]')]) + assert repr(dt) == ( + "dtype([('a', '<M8[D]'), ('b', '<m8[us]')])") + + class AppTestNotDirect(BaseNumpyAppTest): def setup_class(cls): BaseNumpyAppTest.setup_class.im_func(cls) @@ -1332,4 +1507,45 @@ assert a[0] == 1 assert (a + a)[1] == 4 +class AppTestMonsterType(BaseNumpyAppTest): + """Test deeply nested subtypes.""" + def test1(self): + import numpy as np + simple1 = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'], + 'titles': ['Red pixel', 'Blue pixel']}) + a = np.dtype([('yo', int), ('ye', simple1), + ('yi', np.dtype((int, (3, 2))))]) + b = np.dtype([('yo', int), ('ye', simple1), + ('yi', np.dtype((int, (3, 2))))]) + assert a == b + c = np.dtype([('yo', int), ('ye', simple1), + ('yi', np.dtype((a, (3, 2))))]) + d = np.dtype([('yo', int), ('ye', simple1), + ('yi', np.dtype((a, (3, 2))))]) + assert c == d + + +class AppTestMetadata(BaseNumpyAppTest): + def test_no_metadata(self): + import numpy as np + d = np.dtype(int) + assert d.metadata is None + + def test_metadata_takes_dict(self): + import numpy as np + d = np.dtype(int, metadata={'datum': 1}) + assert d.metadata == {'datum': 1} + + def test_metadata_rejects_nondict(self): + import numpy as np + raises(TypeError, np.dtype, int, metadata='datum') + raises(TypeError, np.dtype, int, metadata=1) + raises(TypeError, np.dtype, int, metadata=None) + + def test_nested_metadata(self): + import numpy as np + d = np.dtype([('a', np.dtype(int, metadata={'datum': 1}))]) + assert d['a'].metadata == {'datum': 1} + + diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -764,6 +764,8 @@ assert (a[1:] == b).all() assert (a[1:,newaxis] == d).all() assert (a[newaxis,1:] == c).all() + assert a.strides == (8,) + assert a[:, newaxis].strides == (8, 0) def test_newaxis_assign(self): from numpy import array, newaxis @@ -2345,6 +2347,7 @@ assert a[1] == 0xff assert len(a.data) == 16 assert type(a.data) is buffer + assert a[1:].data._pypy_raw_address() - a.data._pypy_raw_address() == a.strides[0] def test_explicit_dtype_conversion(self): from numpy import array diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py --- a/pypy/module/micronumpy/types.py +++ b/pypy/module/micronumpy/types.py @@ -2396,7 +2396,7 @@ dt = item.arr.dtype ret_unwrapped = [] for name in dt.names: - ofs, dtype = dt.fields[name] + ofs, dtype = dt.fields[name[0]] # XXX: code duplication with W_VoidBox.descr_getitem() if isinstance(dtype.itemtype, VoidType): read_val = dtype.itemtype.readarray(item.arr, ofs, 0, dtype) @@ -2431,14 +2431,14 @@ return w_item else: # match up the field names - items_w = [None] * len(dtype.fields) - for i in range(len(dtype.fields)): + items_w = [None] * len(dtype.names) + for i in range(len(dtype.names)): name = dtype.names[i] if name in w_item.dtype.names: - items_w[i] = w_item.descr_getitem(space, space.wrap(name)) + items_w[i] = w_item.descr_getitem(space, space.wrap(name[0])) elif w_item is not None: if space.isinstance_w(w_item, space.w_tuple): - if len(dtype.fields) != space.len_w(w_item): + if len(dtype.names) != space.len_w(w_item): raise OperationError(space.w_ValueError, space.wrap( "size of tuple must match number of fields.")) items_w = space.fixedview(w_item) @@ -2449,12 +2449,12 @@ "expected a readable buffer object") else: # XXX support initializing from readable buffers - items_w = [w_item] * len(dtype.fields) + items_w = [w_item] * len(dtype.names) else: items_w = [None] * len(dtype.fields) arr = VoidBoxStorage(dtype.elsize, dtype) - for i in range(len(dtype.fields)): - ofs, subdtype = dtype.fields[dtype.names[i]] + for i in range(len(dtype.names)): + ofs, subdtype = dtype.fields[dtype.names[i][0]] try: w_box = subdtype.coerce(space, items_w[i]) except IndexError: @@ -2492,7 +2492,7 @@ items = [] dtype = box.dtype for name in dtype.names: - ofs, subdtype = dtype.fields[name] + ofs, subdtype = dtype.fields[name[0]] subbox = subdtype.read(box.arr, box.ofs, ofs) items.append(subdtype.itemtype.to_builtin_type(space, subbox)) return space.newtuple(items) @@ -2503,7 +2503,7 @@ pieces = ["("] first = True for name in box.dtype.names: - ofs, subdtype = box.dtype.fields[name] + ofs, subdtype = box.dtype.fields[name[0]] if first: first = False else: diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -72,6 +72,10 @@ def get_module(self): return w_some_obj() + + def getname(self, space): + return self.name + def w_some_obj(): if NonConstant(False): return W_Root() diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -1031,6 +1031,48 @@ A.__dict__['x'] = 5 assert A.x == 5 + def test_we_already_got_one_1(self): + # Issue #2079: highly obscure: CPython complains if we say + # ``__slots__="__dict__"`` and there is already a __dict__... + # but from the "best base" only. If the __dict__ comes from + # another base, it doesn't complain. Shrug and copy the logic. + class A(object): + __slots__ = () + class B(object): + pass + class C(A, B): # "best base" is A + __slots__ = ("__dict__",) + class D(A, B): # "best base" is A + __slots__ = ("__weakref__",) + try: + class E(B, A): # "best base" is B + __slots__ = ("__dict__",) + except TypeError, e: + assert 'we already got one' in str(e) + else: + raise AssertionError("TypeError not raised") + try: + class F(B, A): # "best base" is B + __slots__ = ("__weakref__",) + except TypeError, e: + assert 'we already got one' in str(e) + else: + raise AssertionError("TypeError not raised") + + def test_we_already_got_one_2(self): + class A(object): + __slots__ = () + class B: + pass + class C(A, B): # "best base" is A + __slots__ = ("__dict__",) + class D(A, B): # "best base" is A + __slots__ = ("__weakref__",) + class C(B, A): # "best base" is A + __slots__ = ("__dict__",) + class D(B, A): # "best base" is A + __slots__ = ("__weakref__",) + class AppTestWithMethodCacheCounter: spaceconfig = {"objspace.std.withmethodcachecounter": True} diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -1022,7 +1022,7 @@ w_self.nslots = w_bestbase.nslots return hasoldstylebase -def create_all_slots(w_self, hasoldstylebase): +def create_all_slots(w_self, hasoldstylebase, w_bestbase): space = w_self.space dict_w = w_self.dict_w if '__slots__' not in dict_w: @@ -1040,12 +1040,12 @@ for w_slot_name in slot_names_w: slot_name = space.str_w(w_slot_name) if slot_name == '__dict__': - if wantdict or w_self.hasdict: + if wantdict or w_bestbase.hasdict: raise oefmt(space.w_TypeError, "__dict__ slot disallowed: we already got one") wantdict = True elif slot_name == '__weakref__': - if wantweakref or w_self.weakrefable: + if wantweakref or w_bestbase.weakrefable: raise oefmt(space.w_TypeError, "__weakref__ slot disallowed: we already got one") wantweakref = True @@ -1106,7 +1106,7 @@ w_self.flag_abstract |= w_base.flag_abstract hasoldstylebase = copy_flags_from_bases(w_self, w_bestbase) - create_all_slots(w_self, hasoldstylebase) + create_all_slots(w_self, hasoldstylebase, w_bestbase) ensure_common_attributes(w_self) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -8637,5 +8637,27 @@ """ self.optimize_loop(ops, expected, preamble) + def test_getfield_proven_constant(self): + py.test.skip("not working") + ops = """ + [p0] + i1 = getfield_gc(p0, descr=valuedescr) + guard_value(i1, 13) [] + escape(i1) + jump(p0) + """ + expected = """ + [p0] + escape(13) + jump(p0) + """ + expected_short = """ + [p0] + i1 = getfield_gc(p0, descr=valuedescr) + guard_value(i1, 13) [] + jump(p0) + """ + self.optimize_loop(ops, expected, expected_short=expected_short) + class TestLLtype(OptimizeOptTest, LLtypeMixin): pass _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit