Author: Matti Picus <matti.pi...@gmail.com> Branch: unicode-utf8-py3 Changeset: r95930:7b314aebd25c Date: 2019-02-09 20:17 +0100 http://bitbucket.org/pypy/pypy/changeset/7b314aebd25c/
Log: merge py3.5 into branch diff too long, truncating to 2000 out of 3827 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .venv .cache diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,6 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, str): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.values())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, str): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def __next__(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, int): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,113 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `<pwd.h>'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import _thread -_lock = _thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd(metaclass=_structseq.structseqtype): - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - -@builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - -@builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - -@builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) 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 @@ -5,16 +5,17 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f -.. branch: unicode-utf8-re +.. branch: zlib-copying-redux -.. branch: utf8-io +Fix calling copy on already-flushed compressobjs. -Utf8 handling for unicode +.. branch: zlib-copying -.. branch: pyparser-improvements-3 -Small refactorings in the Python parser. +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. -.. branch: unicode-utf8 +.. math-improvements -Use utf8 internally to represent unicode +Improve performance of long operations where one of the operands fits into +an int. \ No newline at end of file diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -705,7 +705,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) -@unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): +@unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: @@ -113,20 +113,9 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - zdict=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -158,6 +147,25 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ + try: + self.lock() + try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -203,16 +211,23 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, - method, wbits, memLevel, strategy, zdict) - return stream + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy, + zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) + return w_stream Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -226,7 +241,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS, zdict=None): + def __init__(self, space, stream, zdict, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -236,16 +251,12 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' + + self.stream = stream + self.zdict = zdict + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.eof = False - try: - self.stream = rzlib.inflateInit(wbits, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") - self.zdict = zdict self.register_finalizer(space) def _finalize_(self): @@ -295,6 +306,27 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ + try: + self.lock() + try: + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=space, + stream=copied, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, + zdict=self.zdict, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -331,10 +363,16 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits, zdict) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + try: + stream = rzlib.inflateInit(wbits, zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, zdict, '', '') + return w_stream def default_buffer_size(space): return space.newint(rzlib.OUTPUT_BUFFER_SIZE) @@ -342,6 +380,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -10,6 +10,13 @@ except ImportError: py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app +try: + from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib +except ImportError: + import py; py.test.skip("no zlib C library on this machine") + class AppTestZlib(object): spaceconfig = dict(usemodules=['zlib']) @@ -21,6 +28,8 @@ compression and decompression tests have a little real data to assert against. """ + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_zlib = cls.space.getbuiltinmodule('zlib') expanded = b'some bytes which will be compressed' cls.w_expanded = cls.space.newbytes(expanded) @@ -29,6 +38,22 @@ py.path.local(pypy.__file__).dirpath().dirpath() .join('LICENSE').read()) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_def_buf_size(self): assert self.zlib.DEF_BUF_SIZE >= 0 @@ -330,3 +355,61 @@ dco = zlib.decompressobj(wbits=-zlib.MAX_WBITS, zdict=zdict) uncomp = dco.decompress(comp) + dco.flush() assert zdict == uncomp + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) + + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress(b"xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -437,7 +437,7 @@ return ix -def _pow_ovf2long(space, iv, iw, w_modulus): +def _pow_ovf2long(space, iv, w_iv, iw, w_iw, w_modulus): if space.is_none(w_modulus) and _recover_with_smalllong(space): from pypy.objspace.std.smalllongobject import _pow as _pow_small try: @@ -446,9 +446,12 @@ return _pow_small(space, r_longlong(iv), iw, r_longlong(0)) except (OverflowError, ValueError): pass - from pypy.objspace.std.longobject import W_LongObject - w_iv = W_LongObject.fromint(space, iv) - w_iw = W_LongObject.fromint(space, iw) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_iv is None or not isinstance(w_iv, W_AbstractLongObject): + w_iv = W_LongObject.fromint(space, iv) + if w_iw is None or not isinstance(w_iw, W_AbstractLongObject): + w_iw = W_LongObject.fromint(space, iw) + return w_iv.descr_pow(space, w_iw, w_modulus) @@ -456,7 +459,7 @@ op = getattr(operator, opname, None) assert op or ovf2small - def ovf2long(space, x, y): + def ovf2long(space, x, w_x, y, w_y): """Handle overflowing to smalllong or long""" if _recover_with_smalllong(space): if ovf2small: @@ -468,9 +471,12 @@ b = r_longlong(y) return W_SmallLongObject(op(a, b)) - from pypy.objspace.std.longobject import W_LongObject - w_x = W_LongObject.fromint(space, x) - w_y = W_LongObject.fromint(space, y) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_x is None or not isinstance(w_x, W_AbstractLongObject): + w_x = W_LongObject.fromint(space, x) + if w_y is None or not isinstance(w_y, W_AbstractLongObject): + w_y = W_LongObject.fromint(space, y) + return getattr(w_x, 'descr_' + opname)(space, w_y) return ovf2long @@ -630,12 +636,18 @@ # can't return NotImplemented (space.pow doesn't do full # ternary, i.e. w_modulus.__zpow__(self, w_exponent)), so # handle it ourselves - return _pow_ovf2long(space, x, y, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) try: result = _pow(space, x, y, z) - except (OverflowError, ValueError): - return _pow_ovf2long(space, x, y, w_modulus) + except OverflowError: + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) + except ValueError: + # float result, so let avoid a roundtrip in rbigint. + self = self.descr_float(space) + w_exponent = w_exponent.descr_float(space) + return space.pow(self, w_exponent, space.w_None) + return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) @@ -685,7 +697,7 @@ try: z = ovfcheck(op(x, y)) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: z = op(x, y) return wrapint(space, z) @@ -709,7 +721,7 @@ try: z = ovfcheck(op(y, x)) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) # XXX write a test else: z = op(y, x) return wrapint(space, z) @@ -743,7 +755,7 @@ try: return func(space, x, y) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: return func(space, x, y) elif isinstance(w_other, W_AbstractIntObject): @@ -760,7 +772,7 @@ try: return func(space, y, x) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) else: return func(space, y, x) elif isinstance(w_other, W_AbstractIntObject): diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -234,22 +234,16 @@ descr_gt = _make_descr_cmp('gt') descr_ge = _make_descr_cmp('ge') - def _make_generic_descr_binop_noncommutative(opname): - methname = opname + '_' if opname in ('and', 'or') else opname - descr_rname = 'descr_r' + opname - op = getattr(rbigint, methname) + def descr_sub(self, space, w_other): + if isinstance(w_other, W_IntObject): + return W_LongObject(self.num.int_sub(w_other.int_w(space))) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return W_LongObject(self.num.sub(w_other.asbigint())) - @func_renamer('descr_' + opname) - @delegate_other - def descr_binop(self, space, w_other): - return W_LongObject(op(self.num, w_other.asbigint())) - - @func_renamer(descr_rname) - @delegate_other - def descr_rbinop(self, space, w_other): - return W_LongObject(op(w_other.asbigint(), self.num)) - - return descr_binop, descr_rbinop + @delegate_other + def descr_rsub(self, space, w_other): + return W_LongObject(w_other.asbigint().sub(self.num)) def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -281,28 +275,23 @@ return descr_binop, descr_rbinop descr_add, descr_radd = _make_generic_descr_binop('add') - descr_sub, descr_rsub = _make_generic_descr_binop_noncommutative('sub') + descr_mul, descr_rmul = _make_generic_descr_binop('mul') descr_and, descr_rand = _make_generic_descr_binop('and') descr_or, descr_ror = _make_generic_descr_binop('or') descr_xor, descr_rxor = _make_generic_descr_binop('xor') - def _make_descr_binop(func, int_func=None): + def _make_descr_binop(func, int_func): opname = func.__name__[1:] - if int_func: - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - if isinstance(w_other, W_IntObject): - return int_func(self, space, w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - return func(self, space, w_other) - else: - @delegate_other - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - return func(self, space, w_other) + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_IntObject): + return int_func(self, space, w_other.int_w(space)) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return func(self, space, w_other) + @delegate_other @func_renamer('descr_r' + opname) def descr_rbinop(self, space, w_other): @@ -322,10 +311,10 @@ raise oefmt(space.w_OverflowError, "shift count too large") return W_LongObject(self.num.lshift(shift)) - def _int_lshift(self, space, w_other): - if w_other < 0: + def _int_lshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return W_LongObject(self.num.lshift(w_other)) + return W_LongObject(self.num.lshift(other)) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -338,11 +327,11 @@ raise oefmt(space.w_OverflowError, "shift count too large") return newlong(space, self.num.rshift(shift)) - def _int_rshift(self, space, w_other): - if w_other < 0: + def _int_rshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return newlong(space, self.num.rshift(w_other)) + return newlong(space, self.num.rshift(other)) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): @@ -353,14 +342,14 @@ "long division or modulo by zero") return newlong(space, z) - def _floordiv(self, space, w_other): + def _int_floordiv(self, space, other): try: - z = self.num.floordiv(w_other.asbigint()) + z = self.num.int_floordiv(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return newlong(space, z) - descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv) + descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv, _int_floordiv) def _mod(self, space, w_other): try: @@ -370,9 +359,9 @@ "integer division or modulo by zero") return newlong(space, z) - def _int_mod(self, space, w_other): + def _int_mod(self, space, other): try: - z = self.num.int_mod(w_other) + z = self.num.int_mod(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") @@ -386,7 +375,16 @@ raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return space.newtuple([newlong(space, div), newlong(space, mod)]) - descr_divmod, descr_rdivmod = _make_descr_binop(_divmod) + + def _int_divmod(self, space, other): + try: + div, mod = self.num.int_divmod(other) + except ZeroDivisionError: + raise oefmt(space.w_ZeroDivisionError, + "long division or modulo by zero") + return space.newtuple([newlong(space, div), newlong(space, mod)]) + + descr_divmod, descr_rdivmod = _make_descr_binop(_divmod, _int_divmod) def _hash_long(space, v): diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -738,6 +738,11 @@ raises(ValueError, int, '00000000000000000000000000000000000007', 0) raises(ValueError, int, '00000000000000000077777777777777777777', 0) + def test_some_rops(self): + b = 2 ** 31 + x = -b + assert x.__rsub__(2) == (2 + b) + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -121,6 +121,8 @@ pypydir.ensure('include', dir=True) if sys.platform == 'win32': + os.environ['PATH'] = str(basedir.join('externals').join('bin')) + ';' + \ + os.environ.get('PATH', '') src,tgt = binaries[0] pypyw = src.new(purebasename=src.purebasename + 'w') if pypyw.exists(): diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,7 +1,7 @@ # Edit these appropriately before running this script pmaj=2 # python main version pmin=7 # python minor version -maj=6 +maj=7 min=0 rev=0 branchname=release-pypy$pmaj.$pmin-$maj.x # ==OR== release-$maj.x # ==OR== release-$maj.$min.x @@ -13,7 +13,7 @@ hg log -r $tagname || exit 1 hgrev=`hg id -r $tagname -i` -rel=pypy$pmaj-v$maj.$min.$rev +rel=pypy$pmaj.$pmin-v$maj.$min.$rev # The script should be run in an empty in the pypy tree, i.e. pypy/tmp if [ "`ls . | wc -l`" != "0" ] then @@ -23,7 +23,7 @@ # Download latest builds from the buildmaster, rename the top # level directory, and repackage ready to be uploaded to bitbucket -for plat in linux linux64 linux-armhf-raspbian linux-armel osx64 s390x +for plat in linux linux64 osx64 s390x # linux-armhf-raspbian linux-armel do echo downloading package for $plat if wget -q --show-progress http://buildbot.pypy.org/nightly/$branchname/pypy-c-jit-latest-$plat.tar.bz2 diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1191,14 +1191,14 @@ inet_ntoa = external('inet_ntoa', [in_addr], rffi.CCHARP) -if _POSIX: - inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, - rffi.VOIDP], rffi.INT, - save_err=SAVE_ERR) - inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, - socklen_t], CCHARP, - save_err=SAVE_ERR) +inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, + rffi.VOIDP], rffi.INT, + save_err=SAVE_ERR) + +inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, + socklen_t], CCHARP, + save_err=SAVE_ERR) inet_addr = external('inet_addr', [rffi.CCHARP], rffi.UINT) socklen_t_ptr = lltype.Ptr(rffi.CFixedArray(socklen_t, 1)) diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -612,6 +612,7 @@ r_ulonglong = build_int('r_ulonglong', False, 64) r_longlonglong = build_int('r_longlonglong', True, 128) +r_ulonglonglong = build_int('r_ulonglonglong', False, 128) longlongmax = r_longlong(LONGLONG_TEST - 1) if r_longlong is not r_int: diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -27,6 +27,7 @@ else: UDIGIT_MASK = longlongmask LONG_TYPE = rffi.__INT128_T + ULONG_TYPE = rffi.__UINT128_T if LONG_BIT > SHIFT: STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned @@ -40,6 +41,7 @@ STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned LONG_TYPE = rffi.LONGLONG + ULONG_TYPE = rffi.ULONGLONG MASK = int((1 << SHIFT) - 1) FLOAT_MULTIPLIER = float(1 << SHIFT) @@ -97,6 +99,9 @@ def _widen_digit(x): return rffi.cast(LONG_TYPE, x) +def _unsigned_widen_digit(x): + return rffi.cast(ULONG_TYPE, x) + @specialize.argtype(0) def _store_digit(x): return rffi.cast(STORE_TYPE, x) @@ -108,6 +113,7 @@ NULLDIGIT = _store_digit(0) ONEDIGIT = _store_digit(1) +NULLDIGITS = [NULLDIGIT] def _check_digits(l): for x in l: @@ -133,22 +139,26 @@ def specialize_call(self, hop): hop.exception_cannot_occur() +def intsign(i): + return -1 if i < 0 else 1 class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits"] - - def __init__(self, digits=[NULLDIGIT], sign=0, size=0): + _immutable_fields_ = ["_digits[*]", "size", "sign"] + + def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): _check_digits(digits) make_sure_not_resized(digits) self._digits = digits + assert size >= 0 self.size = size or len(digits) + self.sign = sign - # __eq__ and __ne__ method exist for testingl only, they are not RPython! + # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython def __eq__(self, other): if not isinstance(other, rbigint): @@ -159,6 +169,7 @@ def __ne__(self, other): return not (self == other) + @specialize.argtype(1) def digit(self, x): """Return the x'th digit, as an int.""" return self._digits[x] @@ -170,6 +181,12 @@ return _widen_digit(self._digits[x]) widedigit._always_inline_ = True + def uwidedigit(self, x): + """Return the x'th digit, as a long long int if needed + to have enough room to contain two digits.""" + return _unsigned_widen_digit(self._digits[x]) + uwidedigit._always_inline_ = True + def udigit(self, x): """Return the x'th digit, as an unsigned int.""" return _load_unsigned_digit(self._digits[x]) @@ -183,7 +200,9 @@ setdigit._always_inline_ = True def numdigits(self): - return self.size + w = self.size + assert w > 0 + return w numdigits._always_inline_ = True @staticmethod @@ -196,13 +215,15 @@ if intval < 0: sign = -1 ival = -r_uint(intval) + carry = ival >> SHIFT elif intval > 0: sign = 1 ival = r_uint(intval) + carry = 0 else: return NULLRBIGINT - carry = ival >> SHIFT + if carry: return rbigint([_store_digit(ival & MASK), _store_digit(carry)], sign, 2) @@ -509,23 +530,22 @@ return True @jit.elidable - def int_eq(self, other): + def int_eq(self, iother): """ eq with int """ - - if not int_in_valid_range(other): - # Fallback to Long. - return self.eq(rbigint.fromint(other)) + if not int_in_valid_range(iother): + # Fallback to Long. + return self.eq(rbigint.fromint(iother)) if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == other + return (self.sign * self.digit(0)) == iother def ne(self, other): return not self.eq(other) - def int_ne(self, other): - return not self.int_eq(other) + def int_ne(self, iother): + return not self.int_eq(iother) @jit.elidable def lt(self, other): @@ -563,59 +583,38 @@ return False @jit.elidable - def int_lt(self, other): + def int_lt(self, iother): """ lt where other is an int """ - if not int_in_valid_range(other): + if not int_in_valid_range(iother): # Fallback to Long. - return self.lt(rbigint.fromint(other)) - - osign = 1 - if other == 0: - osign = 0 - elif other < 0: - osign = -1 - - if self.sign > osign: - return False - elif self.sign < osign: - return True - - digits = self.numdigits() - - if digits > 1: - if osign == 1: - return False - else: - return True - - d1 = self.sign * self.digit(0) - if d1 < other: - return True - return False + return self.lt(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, False) def le(self, other): return not other.lt(self) - def int_le(self, other): - # Alternative that might be faster, reimplant this. as a check with other + 1. But we got to check for overflow - # or reduce valid range. - - if self.int_eq(other): - return True - return self.int_lt(other) + def int_le(self, iother): + """ le where iother is an int """ + + if not int_in_valid_range(iother): + # Fallback to Long. + return self.le(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, True) def gt(self, other): return other.lt(self) - def int_gt(self, other): - return not self.int_le(other) + def int_gt(self, iother): + return not self.int_le(iother) def ge(self, other): return not self.lt(other) - def int_ge(self, other): - return not self.int_lt(other) + def int_ge(self, iother): + return not self.int_lt(iother) @jit.elidable def hash(self): @@ -635,20 +634,20 @@ return result @jit.elidable - def int_add(self, other): - if not int_in_valid_range(other): + def int_add(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.add(rbigint.fromint(other)) + return self.add(rbigint.fromint(iother)) elif self.sign == 0: - return rbigint.fromint(other) - elif other == 0: + return rbigint.fromint(iother) + elif iother == 0: return self - sign = -1 if other < 0 else 1 + sign = intsign(iother) if self.sign == sign: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) else: - result = _x_int_sub(self, other) + result = _x_int_sub(self, iother) result.sign *= -1 result.sign *= sign return result @@ -658,7 +657,7 @@ if other.sign == 0: return self elif self.sign == 0: - return rbigint(other._digits[:other.size], -other.sign, other.size) + return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) elif self.sign == other.sign: result = _x_sub(self, other) else: @@ -667,93 +666,94 @@ return result @jit.elidable - def int_sub(self, other): - if not int_in_valid_range(other): + def int_sub(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.sub(rbigint.fromint(other)) - elif other == 0: + return self.sub(rbigint.fromint(iother)) + elif iother == 0: return self elif self.sign == 0: - return rbigint.fromint(-other) - elif self.sign == (-1 if other < 0 else 1): - result = _x_int_sub(self, other) + return rbigint.fromint(-iother) + elif self.sign == intsign(iother): + result = _x_int_sub(self, iother) else: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) result.sign *= self.sign return result @jit.elidable - def mul(self, b): - asize = self.numdigits() - bsize = b.numdigits() - - a = self - - if asize > bsize: - a, b, asize, bsize = b, a, bsize, asize - - if a.sign == 0 or b.sign == 0: + def mul(self, other): + selfsize = self.numdigits() + othersize = other.numdigits() + + if selfsize > othersize: + self, other, selfsize, othersize = other, self, othersize, selfsize + + if self.sign == 0 or other.sign == 0: return NULLRBIGINT - if asize == 1: - if a._digits[0] == ONEDIGIT: - return rbigint(b._digits[:b.size], a.sign * b.sign, b.size) - elif bsize == 1: - res = b.widedigit(0) * a.widedigit(0) + if selfsize == 1: + if self._digits[0] == ONEDIGIT: + return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + elif othersize == 1: + res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], a.sign * b.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) else: - return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1) - - result = _x_mul(a, b, a.digit(0)) + return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + + result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: - if a is b: + if self is other: i = KARATSUBA_SQUARE_CUTOFF else: i = KARATSUBA_CUTOFF - if asize <= i: - result = _x_mul(a, b) - """elif 2 * asize <= bsize: - result = _k_lopsided_mul(a, b)""" + if selfsize <= i: + result = _x_mul(self, other) + """elif 2 * selfsize <= othersize: + result = _k_lopsided_mul(self, other)""" else: - result = _k_mul(a, b) + result = _k_mul(self, other) else: - result = _x_mul(a, b) - - result.sign = a.sign * b.sign + result = _x_mul(self, other) + + result.sign = self.sign * other.sign return result @jit.elidable - def int_mul(self, b): - if not int_in_valid_range(b): + def int_mul(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.mul(rbigint.fromint(b)) - - if self.sign == 0 or b == 0: + return self.mul(rbigint.fromint(iother)) + + if self.sign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() - digit = abs(b) - bsign = -1 if b < 0 else 1 + digit = abs(iother) + + othersign = intsign(iother) if digit == 1: - return rbigint(self._digits[:self.size], self.sign * bsign, asize) + if othersign == 1: + return self + return rbigint(self._digits[:asize], self.sign * othersign, asize) elif asize == 1: - res = self.widedigit(0) * digit + udigit = r_uint(digit) + res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * bsign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * bsign, 1) - + return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * bsign + result.sign = self.sign * othersign return result @jit.elidable @@ -763,12 +763,10 @@ @jit.elidable def floordiv(self, other): - if self.sign == 1 and other.numdigits() == 1 and other.sign == 1: - digit = other.digit(0) - if digit == 1: - return rbigint(self._digits[:self.size], 1, self.size) - elif digit and digit & (digit - 1) == 0: - return self.rshift(ptwotable[digit]) + if other.numdigits() == 1: + otherint = other.digit(0) * other.sign + assert int_in_valid_range(otherint) + return self.int_floordiv(otherint) div, mod = _divrem(self, other) if mod.sign * other.sign == -1: @@ -782,6 +780,37 @@ return self.floordiv(other) @jit.elidable + def int_floordiv(self, iother): + if not int_in_valid_range(iother): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit