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

Reply via email to