# NOT_RPYTHON """This module defines an object type which can efficiently represent an array of basic values: characters, integers, floating point numbers. Arrays are sequence types and behave very much like lists, except that the type of objects stored in them is constrained. The type is specified at object creation time by using a type code, which is a single character. The following type codes are defined: Type code C Type Minimum size in bytes 'c' character 1 'b' signed integer 1 'B' unsigned integer 1 'u' Unicode character 2 'h' signed integer 2 'H' unsigned integer 2 'i' signed integer 2 'I' unsigned integer 2 'l' signed integer 4 'L' unsigned integer 4 'f' floating point 4 'd' floating point 8 The constructor is: array(typecode [, initializer]) -- create a new array """ import sys from struct import pack, unpack if sys.maxunicode == 65535: UNICODE_SIZE = 2 UNICODE_FORMAT = "H" else: UNICODE_SIZE = 4 UNICODE_FORMAT = "I" def c_type_check(x): if not isinstance(x, str) or len(x) != 1: raise TypeError("array item must be char") return x def general_int_type_check(x, lower_bound, upper_bound): if not (isinstance(x, int) or isinstance(x, long) or isinstance(x, float)): raise TypeError("an integer is required") x_int = int(x) if not (x_int >= lower_bound and x_int <= upper_bound): raise OverflowError("integer is not in the allowed range") return x_int def b_type_check(x): return general_int_type_check(x, -128, 127) def BB_type_check(x): return general_int_type_check(x, 0, 255) def u_type_check(x): if isinstance(x, str): if len(x) / UNICODE_SIZE == 1: # XXX Problem: CPython will gladly accept any unicode codepoint # up to 0xFFFFFFFF in UCS-4 builds here, but unichr which is used # to implement decode for unicode_internal in PyPy will # reject anything above 0x00110000. I don't think there is a # way to replicate CPython behaviour without resorting to C. x_uni = x[:UNICODE_SIZE].decode("unicode_internal") else: raise TypeError("array item must be unicode character") elif isinstance(x, unicode): x_uni = x else: raise TypeError("array item must be unicode character") if len(x_uni) != 1: raise TypeError("array item must be unicode character") return x_uni def h_type_check(x): return general_int_type_check(x, -32768, 32767) def HH_type_check(x): return general_int_type_check(x, 0, 65535) def i_type_check(x): return general_int_type_check(x, -2147483648, 2147483647) def general_long_type_check(x, lower_bound, upper_bound): if not (isinstance(x, int) or isinstance(x, long) or isinstance(x, float)): raise TypeError("an integer is required") x_long = long(x) if not (x_long >= lower_bound and x_long <= upper_bound): raise OverflowError("integer/long is not in the allowed range") return x_long def II_type_check(x): return general_long_type_check(x, 0, 4294967295L) l_type_check = i_type_check LL_type_check = II_type_check def f_type_check(x): if not (isinstance(x, int) or isinstance(x, long) or isinstance(x, float)): raise TypeError("a float is required") x_float = float(x) # XXX This is not exactly a nice way of checking bounds. Formerly, I tried # this: return unpack("f", pack("f", x_float))[0] # Which works in CPython, but PyPy struct is not currently intelligent # enough to handle this. if x_float > 3.4028e38: x_float = 3.4028e38 elif x_float < -3.4028e38: x_float = -3.4028e38 return x_float def d_type_check(x): if not (isinstance(x, int) or isinstance(x, long) or isinstance(x, float)): raise TypeError("a float is required") return float(x) def dummy_type_check(x): return x # XXX Assuming fixed sizes of the different types, independent of platform. # These are the same assumptions that struct makes at the moment. descriptors = { 'c' : ('char', 1, c_type_check), 'b' : ('signed char', 1, b_type_check), 'B' : ('unsigned char', 1, BB_type_check), 'u' : ('Py_UNICODE', UNICODE_SIZE, u_type_check), 'h' : ('signed short', 2, h_type_check), 'H' : ('unsigned short', 2, HH_type_check), 'i' : ('signed int', 4, i_type_check), 'I' : ('unsigned int', 4, II_type_check), 'l' : ('signed long', 4, l_type_check), 'L' : ('unsigned long', 4, LL_type_check), 'f' : ('float', 4, f_type_check), 'd' : ('double', 8, d_type_check), } class array(object): """array(typecode [, initializer]) -> array Return a new array whose items are restricted by typecode, and initialized from the optional initializer value, which must be a list, string. or iterable over elements of the appropriate type. Arrays represent basic values and behave very much like lists, except the type of objects stored in them is constrained. Methods: append() -- append a new item to the end of the array buffer_info() -- return information giving the current memory info byteswap() -- byteswap all the items of the array count() -- return number of occurences of an object extend() -- extend array by appending multiple elements from an iterable fromfile() -- read items from a file object fromlist() -- append items from the list fromstring() -- append items from the string index() -- return index of first occurence of an object insert() -- insert a new item into the array at a provided position pop() -- remove and return item (default last) read() -- DEPRECATED, use fromfile() remove() -- remove first occurence of an object reverse() -- reverse the order of the items in the array tofile() -- write all items to a file object tolist() -- return the array converted to an ordinary list tostring() -- return the array converted to a string write() -- DEPRECATED, use tofile() Attributes: typecode -- the typecode character used to create the array itemsize -- the length in bytes of one array item """ __slots__ = ["typecode", "itemsize", "_data", "_descriptor", "__weakref__"] def __new__(cls, typecode, initializer=[]): self = object.__new__(cls) if not isinstance(typecode, str) or len(typecode) != 1: raise TypeError( "array() argument 1 must be char, not %s" % type(typecode)) if not descriptors.has_key(typecode): raise ValueError( "bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)") self._descriptor = descriptors[typecode] self._data = [] self.typecode = typecode self.itemsize = self._descriptor[1] if isinstance(initializer, list): self.fromlist(initializer) elif isinstance(initializer, str): self.fromstring(initializer) elif isinstance(initializer, unicode) and self.typecode == "u": self.fromunicode(initializer) else: self.extend(initializer) return self ##### array-specific operations def fromfile(self, f, n): """Read n objects from the file object f and append them to the end of the array. Also called as read.""" if not isinstance(f, file): raise TypeError("arg1 must be open file") for i in range(n): item = f.read(self.itemsize) if len(item) < self.itemsize: raise EOFError("not enough items in file") self.fromstring(item) def fromlist(self, l): """Append items to array from list.""" if not isinstance(l, list): raise TypeError("arg must be list") self._fromiterable(l) def fromstring(self, s): """Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method).""" if isinstance(s, unicode): s = str(s) if not (isinstance(s, str) or isinstance(s, buffer)): raise TypeError( "fromstring() argument 1 must be string or read-only buffer") if len(s) % self.itemsize != 0: raise ValueError("string length not a multiple of item size") items = [] if self.typecode == "u": for i in range(0, len(s), self.itemsize): items.append(u_type_check(s[i:i + self.itemsize])) else: for i in range(0, len(s), self.itemsize): item = unpack(self.typecode, s[i:i + self.itemsize])[0] # the item needn't be type checked if unpack succeeded items.append(item) self._data.extend(items) def fromunicode(self, ustr): """Extends this array with data from the unicode string ustr. The array must be a type 'u' array; otherwise a ValueError is raised. Use array.fromstring(ustr.encode(...)) to append Unicode data to an array of some other type.""" if not self.typecode == "u": raise ValueError( "fromunicode() may only be called on type 'u' arrays") if isinstance(ustr, unicode): self._fromiterable(ustr) elif isinstance(ustr, str) or isinstance(ustr, buffer): # CPython strangely truncates string arguments at multiples of the # unicode byte size ... trunc_s = ustr[:len(ustr) - (len(ustr) % self.itemsize)] self.fromstring(trunc_s) else: raise TypeError( "fromunicode() argument 1 must be string or read-only buffer") def tofile(self, f): """Write all items (as machine values) to the file object f. Also called as write.""" if not isinstance(f, file): raise TypeError("arg must be open file") f.write(self.tostring()) def tolist(self): """Convert array to an ordinary list with the same items.""" return self._data[:] def tostring(self): """Convert the array to an array of machine values and return the string representation.""" if self.typecode == "u": return u"".join(self._data).encode("unicode_internal") else: strings = [] for item in self._data: strings.append(pack(self.typecode, item)) return "".join(strings) def tounicode(self): """Convert the array to a unicode string. The array must be a type 'u' array; otherwise a ValueError is raised. Use array.tostring().decode() to obtain a unicode string from an array of some other type.""" if self.typecode != "u": raise ValueError("tounicode() may only be called on type 'u' arrays") return u"".join(self._data) def byteswap(self): """Byteswap all items of the array. If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is raised.""" if self.itemsize not in [1, 2, 4, 8]: raise RuntimeError("byteswap not supported for this array") bytes = self.tostring() swapped = [] for offset in range(0, len(self._data) * self.itemsize, self.itemsize): l = list(bytes[offset:offset + self.itemsize]) l.reverse() swapped += l self._data = [] self.fromstring("".join(swapped)) def buffer_info(self): """Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array's contents. The length should be multiplied by the itemsize attribute to calculate the buffer length in bytes. In PyPy the return values are not really meaningful.""" return (0, len(self._data)) read = fromfile write = tofile ##### general object protocol def __repr__(self): if len(self._data) == 0: return "array('%s')" % self.typecode elif self.typecode == "c": return "array('%s', %s)" % (self.typecode, repr(self.tostring())) elif self.typecode == "u": return "array('%s', %s)" % (self.typecode, repr(self.tounicode())) else: return "array('%s', %s)" % (self.typecode, repr(self.tolist())) def __copy__(self): a = array(self.typecode) a._data = self._data[:] return a def __eq__(self, other): if not isinstance(other, array): return NotImplemented return self._data == other._data def __ne__(self, other): if not isinstance(other, array): return NotImplemented return self._data != other._data def __lt__(self, other): if not isinstance(other, array): return NotImplemented return self._data < other._data def __gt__(self, other): if not isinstance(other, array): return NotImplemented return self._data > other._data def __le__(self, other): if not isinstance(other, array): return NotImplemented return self._data <= other._data def __ge__(self, other): if not isinstance(other, array): return NotImplemented return self._data >= other._data ##### list methods def append(self, x): """Append new value x to the end of the array.""" x_checked = self._type_check(x) self._data.append(x_checked) def count(self, x): """Return number of occurences of x in the array.""" return self._data.count(x) def extend(self, iterable): """Append items to the end of the array.""" if isinstance(iterable, array) \ and not self.typecode == iterable.typecode: raise TypeError("can only extend with array of same kind") self._fromiterable(iterable) def index(self, x): """Return index of first occurence of x in the array.""" return self._data.index(x) def insert(self, i, x): """Insert a new item x into the array before position i.""" x_checked = self._type_check(x) self._data.insert(i, x_checked) def pop(self, i=-1): """Return the i-th element and delete it from the array. i defaults to -1.""" return self._data.pop(i) def remove(self, x): """Remove the first occurence of x in the array.""" self._data.remove(x) def reverse(self): """Reverse the order of the items in the array.""" self._data.reverse() ##### list protocol def __len__(self): return len(self._data) def __add__(self, other): if not isinstance(other, array): raise TypeError("can only append array to array") if self.typecode != other.typecode: raise TypeError("bad argument type for built-in operation") return array(self.typecode, self._data + other._data) def __mul__(self, repeat): return array(self.typecode, self._data * repeat) __rmul__ = __mul__ def __getitem__(self, i): if isinstance(i, slice): sliced = array(self.typecode) sliced._data = self._data[i] return sliced return self._data[i] def __getslice__(self, i, j): return self.__getitem__(slice(i, j)) def __setitem__(self, i, x): if isinstance(i, slice): if not isinstance(x, array): raise TypeError("can only assign array to array slice") if x.typecode != self.typecode: raise TypeError("bad argument type for built-in operation") self._data[i] = x._data return if not (isinstance(i, int) or isinstance(i, long)): raise TypeError("list indices must be integers") if i >= len(self._data) or i < -len(self._data): raise IndexError("array assignment index out of range") x_checked = self._type_check(x) self._data[i] = x_checked def __setslice__(self, i, j, x): self.__setitem__(slice(i, j), x) def __delitem__(self, i): del self._data[i] def __delslice__(self, i, j): self.__delitem__(slice(i, j)) def __contains__(self, item): return item in self._data def __iadd__(self, other): if not isinstance(other, array): raise TypeError("can only extend array with array") self.extend(other) return self def __imul__(self, repeat): self._data *= repeat return self def __iter__(self): return iter(self._data) ##### internal methods def _type_check(self, x): return self._descriptor[2](x) def _fromiterable(self, iterable): items = [] for item in iterable: item_checked = self._type_check(item) items.append(item_checked) self._data.extend(items) ArrayType = array