Hello community,

here is the log from the commit of package python-PyMySQL for openSUSE:Factory 
checked in at 2017-08-18 15:04:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-PyMySQL (Old)
 and      /work/SRC/openSUSE:Factory/.python-PyMySQL.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-PyMySQL"

Fri Aug 18 15:04:20 2017 rev:9 rq:517107 version:0.7.11

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-PyMySQL/python-PyMySQL.changes    
2016-12-08 00:29:16.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-PyMySQL.new/python-PyMySQL.changes       
2017-08-18 15:04:30.513270999 +0200
@@ -1,0 +2,25 @@
+Wed Aug 16 01:36:37 UTC 2017 - toddrme2...@gmail.com
+
+- Implement single-spec version
+- update to 0.7.11
+  * Fixed Connection.close() failed when failed to send COM_CLOSE packet.
+  * Cursor.executemany() accepts query ends with semicolon.
+  * ssl parameters can be read from my.cnf.
+- update to 0.7.10
+  * **SECURITY FIX**: Raise RuntimeError when received LOAD_LOCAL packet while
+    ``loacal_infile=False``.  (Thanks to Bryan Helmig)
+  * Raise SERVER_LOST error for MariaDB's shutdown packet (#540)
+  * Change default connect_timeout to 10.
+  * Add bind_address option (#529)
+- update to 0.7.9
+  * Fix PyMySQL stop reading rows when first column is empty string (#513)
+    Reverts DEPRECATE_EOF introduced in 0.7.7.
+- update to 0.7.8
+  * Revert error message change in 0.7.7.
+    (SQLAlchemy parses error message, #507)
+- update to 0.7.7
+  * Add new unicode collation (#498)
+  * Fix conv option is not used for encoding objects.
+  * Experimental support for DEPRECATE_EOF protocol.
+
+-------------------------------------------------------------------

Old:
----
  PyMySQL-0.7.6.tar.gz

New:
----
  PyMySQL-0.7.11.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-PyMySQL.spec ++++++
--- /var/tmp/diff_new_pack.o9xpIu/_old  2017-08-18 15:04:33.028916689 +0200
+++ /var/tmp/diff_new_pack.o9xpIu/_new  2017-08-18 15:04:33.040914999 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-PyMySQL
 #
-# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,28 +16,28 @@
 #
 
 
+%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-PyMySQL
-Version:        0.7.6
+Version:        0.7.11
 Release:        0
 Summary:        Pure Python MySQL Driver
 License:        MIT
 Group:          Development/Languages/Python
-Url:            http://code.google.com/p/pymysql
-Source:         
https://pypi.io/packages/source/P/PyMySQL/PyMySQL-%{version}.tar.gz
-BuildRequires:  python-devel
-BuildRequires:  python-setuptools
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
-%else
+Url:            https://github.com/PyMySQL/PyMySQL/
+Source:         
https://files.pythonhosted.org/packages/source/P/PyMySQL/PyMySQL-%{version}.tar.gz
+BuildRequires:  %{python_module devel}
+BuildRequires:  %{python_module setuptools}
+BuildRequires:  fdupes
+BuildRequires:  python-rpm-macros
 BuildArch:      noarch
-%endif
+
+%python_subpackages
 
 %description
 This package contains a pure-Python MySQL client library. Documentation on the
 MySQL client/server protocol can be found here:
-http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol If you would
-like to run the test suite, edit the config parameters in 
pymysql/tests/base.py.
+http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol
+
 The goal of pymysql is to be a drop-in replacement for MySQLdb and work on
 CPython 2.3+, Jython, IronPython, PyPy and Python 3. We test for compatibility
 by simply changing the import statements in the Django MySQL backend and 
running
@@ -49,15 +49,16 @@
 sed -i '1 { /^#!/ d }' pymysql/tests/thirdparty/test_MySQLdb/*.py
 
 %build
-python setup.py build
+%python_build
 
 %install
-python setup.py install --prefix=%{_prefix} --root=%{buildroot}
+%python_install
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 #%%check
 #NOTE(saschpe): Needs mysql server
 
-%files
+%files %{python_files}
 %defattr(-,root,root,-)
 %doc CHANGELOG LICENSE README.rst 
 %{python_sitelib}/*

++++++ PyMySQL-0.7.6.tar.gz -> PyMySQL-0.7.11.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/CHANGELOG new/PyMySQL-0.7.11/CHANGELOG
--- old/PyMySQL-0.7.6/CHANGELOG 2016-07-29 05:21:41.000000000 +0200
+++ new/PyMySQL-0.7.11/CHANGELOG        2017-04-05 20:05:20.000000000 +0200
@@ -1,5 +1,50 @@
 # Changes
 
+## 0.7.11
+
+Release date: 2017-04-06
+
+* Fixed Connection.close() failed when failed to send COM_CLOSE packet.
+* Cursor.executemany() accepts query ends with semicolon.
+* ssl parameters can be read from my.cnf.
+
+
+## 0.7.10
+
+Release date: 2017-02-14
+
+* **SECURITY FIX**: Raise RuntimeError when received LOAD_LOCAL packet while
+  ``loacal_infile=False``.  (Thanks to Bryan Helmig)
+
+* Raise SERVER_LOST error for MariaDB's shutdown packet (#540)
+
+* Change default connect_timeout to 10.
+
+* Add bind_address option (#529)
+
+
+## 0.7.9
+
+Release date: 2016-09-03
+
+* Fix PyMySQL stop reading rows when first column is empty string (#513)
+  Reverts DEPRECATE_EOF introduced in 0.7.7.
+
+## 0.7.8
+
+Release date: 2016-09-01
+
+* Revert error message change in 0.7.7.
+  (SQLAlchemy parses error message, #507)
+
+## 0.7.7
+
+Release date: 2016-08-30
+
+* Add new unicode collation (#498)
+* Fix conv option is not used for encoding objects.
+* Experimental support for DEPRECATE_EOF protocol.
+
 ## 0.7.6
 
 Release date: 2016-07-29
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/PKG-INFO new/PyMySQL-0.7.11/PKG-INFO
--- old/PyMySQL-0.7.6/PKG-INFO  2016-07-29 05:27:02.000000000 +0200
+++ new/PyMySQL-0.7.11/PKG-INFO 2017-04-05 20:11:44.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: PyMySQL
-Version: 0.7.6
+Version: 0.7.11
 Summary: Pure Python MySQL Driver
 Home-page: https://github.com/PyMySQL/PyMySQL/
 Author: INADA Naoki
@@ -14,6 +14,7 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Intended Audience :: Developers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/PyMySQL.egg-info/PKG-INFO 
new/PyMySQL-0.7.11/PyMySQL.egg-info/PKG-INFO
--- old/PyMySQL-0.7.6/PyMySQL.egg-info/PKG-INFO 2016-07-29 05:27:02.000000000 
+0200
+++ new/PyMySQL-0.7.11/PyMySQL.egg-info/PKG-INFO        2017-04-05 
20:11:44.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: PyMySQL
-Version: 0.7.6
+Version: 0.7.11
 Summary: Pure Python MySQL Driver
 Home-page: https://github.com/PyMySQL/PyMySQL/
 Author: INADA Naoki
@@ -14,6 +14,7 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Intended Audience :: Developers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/PyMySQL.egg-info/SOURCES.txt 
new/PyMySQL-0.7.11/PyMySQL.egg-info/SOURCES.txt
--- old/PyMySQL-0.7.6/PyMySQL.egg-info/SOURCES.txt      2016-07-29 
05:27:02.000000000 +0200
+++ new/PyMySQL-0.7.11/PyMySQL.egg-info/SOURCES.txt     2017-04-05 
20:11:44.000000000 +0200
@@ -38,6 +38,7 @@
 pymysql/tests/test_connection.py
 pymysql/tests/test_converters.py
 pymysql/tests/test_cursor.py
+pymysql/tests/test_err.py
 pymysql/tests/test_issues.py
 pymysql/tests/test_load_local.py
 pymysql/tests/test_nextset.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/README.rst 
new/PyMySQL-0.7.11/README.rst
--- old/PyMySQL-0.7.6/README.rst        2016-07-28 17:42:45.000000000 +0200
+++ new/PyMySQL-0.7.11/README.rst       2017-02-14 13:21:45.000000000 +0100
@@ -22,10 +22,11 @@
 is to be a drop-in replacement for MySQLdb and work on CPython, PyPy and 
IronPython.
 
 NOTE: PyMySQL doesn't support low level APIs `_mysql` provides like 
`data_seek`,
-`store_result`, and `use_result`. You should use high level APIs defined in 
PEP 294.
-But some APIs like `autocommit` and `ping` are supported because PEP 294 
doesn't cover
+`store_result`, and `use_result`. You should use high level APIs defined in 
`PEP 249`_.
+But some APIs like `autocommit` and `ping` are supported because `PEP 249`_ 
doesn't cover
 their usecase.
 
+.. _`PEP 249`: https://www.python.org/dev/peps/pep-0249/
 
 Requirements
 -------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/__init__.py 
new/PyMySQL-0.7.11/pymysql/__init__.py
--- old/PyMySQL-0.7.6/pymysql/__init__.py       2016-07-29 05:21:53.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/__init__.py      2017-04-05 20:05:40.000000000 
+0200
@@ -26,14 +26,16 @@
 from ._compat import PY2
 from .constants import FIELD_TYPE
 from .converters import escape_dict, escape_sequence, escape_string
-from .err import Warning, Error, InterfaceError, DataError, \
-     DatabaseError, OperationalError, IntegrityError, InternalError, \
-     NotSupportedError, ProgrammingError, MySQLError
-from .times import Date, Time, Timestamp, \
-    DateFromTicks, TimeFromTicks, TimestampFromTicks
+from .err import (
+    Warning, Error, InterfaceError, DataError,
+    DatabaseError, OperationalError, IntegrityError, InternalError,
+    NotSupportedError, ProgrammingError, MySQLError)
+from .times import (
+    Date, Time, Timestamp,
+    DateFromTicks, TimeFromTicks, TimestampFromTicks)
 
 
-VERSION = (0, 7, 6, None)
+VERSION = (0, 7, 11, None)
 threadsafety = 1
 apilevel = "2.0"
 paramstyle = "pyformat"
@@ -87,7 +89,7 @@
     from .connections import Connection
     return Connection(*args, **kwargs)
 
-from pymysql import connections as _orig_conn
+from . import connections as _orig_conn
 if _orig_conn.Connection.__init__.__doc__ is not None:
     Connect.__doc__ = _orig_conn.Connection.__init__.__doc__
 del _orig_conn
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/charset.py 
new/PyMySQL-0.7.11/pymysql/charset.py
--- old/PyMySQL-0.7.6/pymysql/charset.py        2016-07-28 17:42:45.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/charset.py       2016-08-27 19:59:17.000000000 
+0200
@@ -253,6 +253,10 @@
 _charsets.add(Charset(241, 'utf8mb4', 'utf8mb4_esperanto_ci', ''))
 _charsets.add(Charset(242, 'utf8mb4', 'utf8mb4_hungarian_ci', ''))
 _charsets.add(Charset(243, 'utf8mb4', 'utf8mb4_sinhala_ci', ''))
+_charsets.add(Charset(244, 'utf8mb4', 'utf8mb4_german2_ci', ''))
+_charsets.add(Charset(245, 'utf8mb4', 'utf8mb4_croatian_ci', ''))
+_charsets.add(Charset(246, 'utf8mb4', 'utf8mb4_unicode_520_ci', ''))
+_charsets.add(Charset(247, 'utf8mb4', 'utf8mb4_vietnamese_ci', ''))
 
 
 charset_by_name = _charsets.by_name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/connections.py 
new/PyMySQL-0.7.11/pymysql/connections.py
--- old/PyMySQL-0.7.6/pymysql/connections.py    2016-07-28 17:42:45.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/connections.py   2017-04-05 20:02:01.000000000 
+0200
@@ -17,7 +17,7 @@
 import warnings
 
 from .charset import MBLENGTH, charset_by_name, charset_by_id
-from .constants import CLIENT, COMMAND, FIELD_TYPE, SERVER_STATUS
+from .constants import CLIENT, COMMAND, CR, FIELD_TYPE, SERVER_STATUS
 from .converters import escape_item, escape_string, through, conversions as 
_conv
 from .cursors import Cursor
 from .optionfile import Parser
@@ -117,20 +117,18 @@
 
     try:
         print("packet length:", len(data))
-        print("method call[1]:", sys._getframe(1).f_code.co_name)
-        print("method call[2]:", sys._getframe(2).f_code.co_name)
-        print("method call[3]:", sys._getframe(3).f_code.co_name)
-        print("method call[4]:", sys._getframe(4).f_code.co_name)
-        print("method call[5]:", sys._getframe(5).f_code.co_name)
-        print("-" * 88)
+        for i in range(1, 6):
+            f = sys._getframe(i)
+            print("call[%d]: %s (line %d)" % (i, f.f_code.co_name, f.f_lineno))
+        print("-" * 66)
     except ValueError:
         pass
     dump_data = [data[i:i+16] for i in range_type(0, min(len(data), 256), 16)]
     for d in dump_data:
         print(' '.join(map(lambda x: "{:02X}".format(byte2int(x)), d)) +
               '   ' * (16 - len(d)) + ' ' * 2 +
-              ' '.join(map(lambda x: "{}".format(is_ascii(x)), d)))
-    print("-" * 88)
+              ''.join(map(lambda x: "{}".format(is_ascii(x)), d)))
+    print("-" * 66)
     print()
 
 
@@ -363,17 +361,18 @@
         return result
 
     def is_ok_packet(self):
-        return self._data[0:1] == b'\0'
-
-    def is_auth_switch_request(self):
-        # 
http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
-        return self._data[0:1] == b'\xfe'
+        # https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
+        return self._data[0:1] == b'\0' and len(self._data) >= 7
 
     def is_eof_packet(self):
         # 
http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-EOF_Packet
         # Caution: \xFE may be LengthEncodedInteger.
         # If \xFE is LengthEncodedInteger header, 8bytes followed.
-        return len(self._data) < 9 and self._data[0:1] == b'\xfe'
+        return self._data[0:1] == b'\xfe' and len(self._data) < 9
+
+    def is_auth_switch_request(self):
+        # 
http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
+        return self._data[0:1] == b'\xfe'
 
     def is_resultset_packet(self):
         field_count = ord(self._data[0:1])
@@ -525,17 +524,19 @@
 
     _sock = None
     _auth_plugin_name = ''
+    _closed = False
 
     def __init__(self, host=None, user=None, password="",
                  database=None, port=0, unix_socket=None,
                  charset='', sql_mode=None,
                  read_default_file=None, conv=None, use_unicode=None,
                  client_flag=0, cursorclass=Cursor, init_command=None,
-                 connect_timeout=None, ssl=None, read_default_group=None,
+                 connect_timeout=10, ssl=None, read_default_group=None,
                  compress=None, named_pipe=None, no_delay=None,
                  autocommit=False, db=None, passwd=None, local_infile=False,
                  max_allowed_packet=16*1024*1024, defer_connect=False,
-                 auth_plugin_map={}, read_timeout=None, write_timeout=None):
+                 auth_plugin_map={}, read_timeout=None, write_timeout=None,
+                 bind_address=None):
         """
         Establish a connection to the MySQL database. Accepts several
         arguments:
@@ -545,6 +546,9 @@
         password: Password to use.
         database: Database to use, None to not use a particular one.
         port: MySQL port to use, default is usually OK. (default: 3306)
+        bind_address: When the client has multiple network interfaces, specify
+            the interface from which to connect to the host. Argument can be
+            a hostname or an IP address.
         unix_socket: Optionally, you can use a unix socket rather than TCP/IP.
         charset: Charset you want to use.
         sql_mode: Default SQL_MODE to use.
@@ -561,6 +565,7 @@
         cursorclass: Custom cursor class to use.
         init_command: Initial SQL statement to run when connection is 
established.
         connect_timeout: Timeout before throwing an exception when connecting.
+            (default: 10, min: 1, max: 31536000)
         ssl:
             A dict of arguments similar to mysql_ssl_set()'s parameters.
             For now the capath and cipher arguments are not supported.
@@ -595,17 +600,10 @@
         if compress or named_pipe:
             raise NotImplementedError("compress and named_pipe arguments are 
not supported")
 
-        if local_infile:
+        self._local_infile = bool(local_infile)
+        if self._local_infile:
             client_flag |= CLIENT.LOCAL_FILES
 
-        self.ssl = False
-        if ssl:
-            if not SSL_ENABLED:
-                raise NotImplementedError("ssl module not found")
-            self.ssl = True
-            client_flag |= CLIENT.SSL
-            self.ctx = self._create_ssl_ctx(ssl)
-
         if read_default_group and not read_default_file:
             if sys.platform.startswith("win"):
                 read_default_file = "c:\\my.ini"
@@ -633,7 +631,23 @@
             database = _config("database", database)
             unix_socket = _config("socket", unix_socket)
             port = int(_config("port", port))
+            bind_address = _config("bind-address", bind_address)
             charset = _config("default-character-set", charset)
+            if not ssl:
+                ssl = {}
+            if isinstance(ssl, dict):
+                for key in ["ca", "capath", "cert", "key", "cipher"]:
+                    value = _config("ssl-" + key, ssl.get(key))
+                    if value:
+                        ssl[key] = value
+
+        self.ssl = False
+        if ssl:
+            if not SSL_ENABLED:
+                raise NotImplementedError("ssl module not found")
+            self.ssl = True
+            client_flag |= CLIENT.SSL
+            self.ctx = self._create_ssl_ctx(ssl)
 
         self.host = host or "localhost"
         self.port = port or 3306
@@ -641,6 +655,10 @@
         self.password = password or ""
         self.db = database
         self.unix_socket = unix_socket
+        self.bind_address = bind_address
+        if not (0 < connect_timeout <= 31536000):
+            raise ValueError("connect_timeout should be >0 and <=31536000")
+        self.connect_timeout = connect_timeout or None
         if read_timeout is not None and read_timeout <= 0:
             raise ValueError("read_timeout should be >= 0")
         self._read_timeout = read_timeout
@@ -659,13 +677,12 @@
 
         self.encoding = charset_by_name(self.charset).encoding
 
-        client_flag |= CLIENT.CAPABILITIES | CLIENT.MULTI_STATEMENTS
+        client_flag |= CLIENT.CAPABILITIES
         if self.db:
             client_flag |= CLIENT.CONNECT_WITH_DB
         self.client_flag = client_flag
 
         self.cursorclass = cursorclass
-        self.connect_timeout = connect_timeout
 
         self._result = None
         self._affected_rows = 0
@@ -707,24 +724,25 @@
 
     def close(self):
         """Send the quit message and close the socket"""
-        if self._sock is None:
+        if self._closed:
             raise err.Error("Already closed")
+        self._closed = True
+        if self._sock is None:
+            return
         send_data = struct.pack('<iB', 1, COMMAND.COM_QUIT)
         try:
             self._write_bytes(send_data)
         except Exception:
             pass
         finally:
-            sock = self._sock
-            self._sock = None
-            self._rfile = None
-            sock.close()
+            self._force_close()
 
     @property
     def open(self):
         return self._sock is not None
 
-    def __del__(self):
+    def _force_close(self):
+        """Close connection without QUIT message"""
         if self._sock:
             try:
                 self._sock.close()
@@ -733,6 +751,8 @@
         self._sock = None
         self._rfile = None
 
+    __del__ = _force_close
+
     def autocommit(self, value):
         self.autocommit_mode = bool(value)
         current = self.get_autocommit()
@@ -876,6 +896,7 @@
         self.encoding = encoding
 
     def connect(self, sock=None):
+        self._closed = False
         try:
             if sock is None:
                 if self.unix_socket and self.host in ('localhost', 
'127.0.0.1'):
@@ -885,10 +906,14 @@
                     self.host_info = "Localhost via UNIX socket"
                     if DEBUG: print('connected using unix_socket')
                 else:
+                    kwargs = {}
+                    if self.bind_address is not None:
+                        kwargs['source_address'] = (self.bind_address, 0)
                     while True:
                         try:
                             sock = socket.create_connection(
-                                (self.host, self.port), self.connect_timeout)
+                                (self.host, self.port), self.connect_timeout,
+                                **kwargs)
                             break
                         except (OSError, IOError) as e:
                             if e.errno == errno.EINTR:
@@ -965,8 +990,15 @@
             btrl, btrh, packet_number = struct.unpack('<HBB', packet_header)
             bytes_to_read = btrl + (btrh << 16)
             if packet_number != self._next_seq_id:
-                raise err.InternalError("Packet sequence number wrong - got %d 
expected %d" %
-                    (packet_number, self._next_seq_id))
+                self._force_close()
+                if packet_number == 0:
+                    # MariaDB sends error packet with seqno==0 when shutdown
+                    raise err.OperationalError(
+                        CR.CR_SERVER_LOST,
+                        "Lost connection to MySQL server during query")
+                raise err.InternalError(
+                    "Packet sequence number wrong - got %d expected %d"
+                    % (packet_number, self._next_seq_id))
             self._next_seq_id = (self._next_seq_id + 1) % 256
 
             recv_data = self._read_bytes(bytes_to_read)
@@ -991,12 +1023,14 @@
             except (IOError, OSError) as e:
                 if e.errno == errno.EINTR:
                     continue
+                self._force_close()
                 raise err.OperationalError(
-                    2013,
+                    CR.CR_SERVER_LOST,
                     "Lost connection to MySQL server during query (%s)" % (e,))
         if len(data) < num_bytes:
+            self._force_close()
             raise err.OperationalError(
-                2013, "Lost connection to MySQL server during query")
+                CR.CR_SERVER_LOST, "Lost connection to MySQL server during 
query")
         return data
 
     def _write_bytes(self, data):
@@ -1004,7 +1038,10 @@
         try:
             self._sock.sendall(data)
         except IOError as e:
-            raise err.OperationalError(2006, "MySQL server has gone away (%r)" 
% (e,))
+            self._force_close()
+            raise err.OperationalError(
+                CR.CR_SERVER_GONE_ERROR,
+                "MySQL server has gone away (%r)" % (e,))
 
     def _read_query_result(self, unbuffered=False):
         if unbuffered:
@@ -1342,6 +1379,9 @@
         self.has_next = ok_packet.has_next
 
     def _read_load_local_packet(self, first_packet):
+        if not self.connection._local_infile:
+            raise RuntimeError(
+                "**WARN**: Received LOAD_LOCAL packet but local_infile option 
is false.")
         load_packet = LoadLocalPacketWrapper(first_packet)
         sender = LoadLocalFile(load_packet.filename, self.connection)
         try:
@@ -1356,12 +1396,16 @@
         self._read_ok_packet(ok_packet)
 
     def _check_packet_is_eof(self, packet):
-        if packet.is_eof_packet():
-            eof_packet = EOFPacketWrapper(packet)
-            self.warning_count = eof_packet.warning_count
-            self.has_next = eof_packet.has_next
-            return True
-        return False
+        if not packet.is_eof_packet():
+            return False
+        #TODO: Support CLIENT.DEPRECATE_EOF
+        # 1) Add DEPRECATE_EOF to CAPABILITIES
+        # 2) Mask CAPABILITIES with server_capabilities
+        # 3) if server_capabilities & CLIENT.DEPRECATE_EOF: use 
OKPacketWrapper instead of EOFPacketWrapper
+        wp = EOFPacketWrapper(packet)
+        self.warning_count = wp.warning_count
+        self.has_next = wp.has_next
+        return True
 
     def _read_result_packet(self, first_packet):
         self.field_count = first_packet.read_length_encoded_integer()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/constants/CLIENT.py 
new/PyMySQL-0.7.11/pymysql/constants/CLIENT.py
--- old/PyMySQL-0.7.6/pymysql/constants/CLIENT.py       2016-03-06 
15:57:08.000000000 +0100
+++ new/PyMySQL-0.7.11/pymysql/constants/CLIENT.py      2016-09-03 
21:04:12.000000000 +0200
@@ -19,11 +19,13 @@
 PS_MULTI_RESULTS = 1 << 18
 PLUGIN_AUTH = 1 << 19
 PLUGIN_AUTH_LENENC_CLIENT_DATA = 1 << 21
-CAPABILITIES = (LONG_PASSWORD | LONG_FLAG | TRANSACTIONS |
-                PROTOCOL_41 | SECURE_CONNECTION | PLUGIN_AUTH |
-                PLUGIN_AUTH_LENENC_CLIENT_DATA)
+CAPABILITIES = (
+    LONG_PASSWORD | LONG_FLAG | PROTOCOL_41 | TRANSACTIONS
+    | SECURE_CONNECTION | MULTI_STATEMENTS | MULTI_RESULTS
+    | PLUGIN_AUTH | PLUGIN_AUTH_LENENC_CLIENT_DATA)
+
 # Not done yet
 CONNECT_ATTRS = 1 << 20
 HANDLE_EXPIRED_PASSWORDS = 1 << 22
 SESSION_TRACK = 1 << 23
-CLIENT_DEPRECATE_EOF = 1 << 24
+DEPRECATE_EOF = 1 << 24
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/constants/CR.py 
new/PyMySQL-0.7.11/pymysql/constants/CR.py
--- old/PyMySQL-0.7.6/pymysql/constants/CR.py   2015-09-29 17:10:42.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/constants/CR.py  2016-08-29 18:14:43.000000000 
+0200
@@ -1,3 +1,4 @@
+# flake8: noqa
 # errmsg.h
 CR_ERROR_FIRST         = 2000
 CR_UNKNOWN_ERROR       = 2000
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/cursors.py 
new/PyMySQL-0.7.11/pymysql/cursors.py
--- old/PyMySQL-0.7.6/pymysql/cursors.py        2016-07-28 17:42:45.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/cursors.py       2017-04-05 20:02:01.000000000 
+0200
@@ -5,7 +5,6 @@
 import warnings
 
 from ._compat import range_type, text_type, PY2
-
 from . import err
 
 
@@ -15,16 +14,16 @@
 RE_INSERT_VALUES = re.compile(
     r"\s*((?:INSERT|REPLACE)\s.+\sVALUES?\s+)" +
     r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))" +
-    r"(\s*(?:ON DUPLICATE.*)?)\Z",
+    r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z",
     re.IGNORECASE | re.DOTALL)
 
 
 class Cursor(object):
-    '''
+    """
     This is the object you use to interact with the database.
-    '''
+    """
 
-    #: Max stetement size which :meth:`executemany` generates.
+    #: Max statement size which :meth:`executemany` generates.
     #:
     #: Max size of allowed statement is max_allowed_packet - 
packet_header_size.
     #: Default value of max_allowed_packet is 1048576.
@@ -33,10 +32,10 @@
     _defer_warnings = False
 
     def __init__(self, connection):
-        '''
+        """
         Do not create an instance of a Cursor yourself. Call
         connections.Connection.cursor().
-        '''
+        """
         self.connection = connection
         self.description = None
         self.rownumber = 0
@@ -48,9 +47,9 @@
         self._warnings_handled = False
 
     def close(self):
-        '''
+        """
         Closing a cursor just exhausts all remaining data.
-        '''
+        """
         conn = self.connection
         if conn is None:
             return
@@ -116,12 +115,12 @@
         if isinstance(args, (tuple, list)):
             if PY2:
                 args = tuple(map(ensure_bytes, args))
-            return tuple(conn.escape(arg) for arg in args)
+            return tuple(conn.literal(arg) for arg in args)
         elif isinstance(args, dict):
             if PY2:
                 args = dict((ensure_bytes(key), ensure_bytes(val)) for
                             (key, val) in args.items())
-            return dict((key, conn.escape(val)) for (key, val) in args.items())
+            return dict((key, conn.literal(val)) for (key, val) in 
args.items())
         else:
             # If it's not a dictionary let's try escaping it anyways.
             # Worst case it will throw a Value error
@@ -274,7 +273,7 @@
         return args
 
     def fetchone(self):
-        ''' Fetch the next row '''
+        """Fetch the next row"""
         self._check_executed()
         if self._rows is None or self.rownumber >= len(self._rows):
             return None
@@ -283,7 +282,7 @@
         return result
 
     def fetchmany(self, size=None):
-        ''' Fetch several rows '''
+        """Fetch several rows"""
         self._check_executed()
         if self._rows is None:
             return ()
@@ -293,7 +292,7 @@
         return result
 
     def fetchall(self):
-        ''' Fetch all the rows '''
+        """Fetch all the rows"""
         self._check_executed()
         if self._rows is None:
             return ()
@@ -404,8 +403,8 @@
     or for connections to remote servers over a slow network.
 
     Instead of copying every row of data into a buffer, this will fetch
-    rows as needed. The upside of this, is the client uses much less memory,
-    and rows are returned much faster when traveling over a slow network,
+    rows as needed. The upside of this is the client uses much less memory,
+    and rows are returned much faster when traveling over a slow network
     or if the result set is very big.
 
     There are limitations, though. The MySQL protocol doesn't support
@@ -444,11 +443,11 @@
         return self._nextset(unbuffered=True)
 
     def read_next(self):
-        """ Read next row """
+        """Read next row"""
         return self._conv_row(self._result._read_rowdata_packet_unbuffered())
 
     def fetchone(self):
-        """ Fetch next row """
+        """Fetch next row"""
         self._check_executed()
         row = self.read_next()
         if row is None:
@@ -477,7 +476,7 @@
         return self.fetchall_unbuffered()
 
     def fetchmany(self, size=None):
-        """ Fetch many """
+        """Fetch many"""
         self._check_executed()
         if size is None:
             size = self.arraysize
@@ -517,4 +516,4 @@
 
 
 class SSDictCursor(DictCursorMixin, SSCursor):
-    """ An unbuffered cursor, which returns results as a dictionary """
+    """An unbuffered cursor, which returns results as a dictionary"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/err.py 
new/PyMySQL-0.7.11/pymysql/err.py
--- old/PyMySQL-0.7.6/pymysql/err.py    2015-09-29 17:10:42.000000000 +0200
+++ new/PyMySQL-0.7.11/pymysql/err.py   2016-09-03 21:04:12.000000000 +0200
@@ -68,10 +68,12 @@
 
 error_map = {}
 
+
 def _map_error(exc, *errors):
     for error in errors:
         error_map[error] = exc
 
+
 _map_error(ProgrammingError, ER.DB_CREATE_EXISTS, ER.SYNTAX_ERROR,
            ER.PARSE_ERROR, ER.NO_SUCH_TABLE, ER.WRONG_DB_NAME,
            ER.WRONG_TABLE_NAME, ER.FIELD_SPECIFIED_TWICE,
@@ -89,32 +91,17 @@
            ER.CON_COUNT_ERROR, ER.TABLEACCESS_DENIED_ERROR,
            ER.COLUMNACCESS_DENIED_ERROR)
 
+
 del _map_error, ER
 
 
-def _get_error_info(data):
+def raise_mysql_exception(data):
     errno = struct.unpack('<h', data[1:3])[0]
     is_41 = data[3:4] == b"#"
     if is_41:
-        # version 4.1
-        sqlstate = data[4:9].decode("utf8", 'replace')
-        errorvalue = data[9:].decode("utf8", 'replace')
-        return (errno, sqlstate, errorvalue)
+        # client protocol 4.1
+        errval = data[9:].decode('utf-8', 'replace')
     else:
-        # version 4.0
-        return (errno, None, data[3:].decode("utf8", 'replace'))
-
-
-def _check_mysql_exception(errinfo):
-    errno, sqlstate, errorvalue = errinfo
-    errorclass = error_map.get(errno, None)
-    if errorclass:
-        raise errorclass(errno, errorvalue)
-
-    # couldn't find the right error number
-    raise InternalError(errno, errorvalue)
-
-
-def raise_mysql_exception(data):
-    errinfo = _get_error_info(data)
-    _check_mysql_exception(errinfo)
+        errval = data[3:].decode('utf-8', 'replace')
+    errorclass = error_map.get(errno, InternalError)
+    raise errorclass(errno, errval)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/tests/__init__.py 
new/PyMySQL-0.7.11/pymysql/tests/__init__.py
--- old/PyMySQL-0.7.6/pymysql/tests/__init__.py 2016-05-26 06:05:45.000000000 
+0200
+++ new/PyMySQL-0.7.11/pymysql/tests/__init__.py        2016-09-03 
21:04:12.000000000 +0200
@@ -5,6 +5,7 @@
 from pymysql.tests.test_connection import *
 from pymysql.tests.test_converters import *
 from pymysql.tests.test_cursor import *
+from pymysql.tests.test_err import *
 from pymysql.tests.test_issues import *
 from pymysql.tests.test_load_local import *
 from pymysql.tests.test_nextset import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/tests/test_basic.py 
new/PyMySQL-0.7.11/pymysql/tests/test_basic.py
--- old/PyMySQL-0.7.6/pymysql/tests/test_basic.py       2016-07-28 
17:42:45.000000000 +0200
+++ new/PyMySQL-0.7.11/pymysql/tests/test_basic.py      2016-08-29 
18:14:43.000000000 +0200
@@ -375,4 +375,5 @@
             cur.execute("drop table if exists no_exists_table")
         self.assertEqual(len(ws), 1)
         self.assertEqual(ws[0].category, pymysql.Warning)
-        self.assertTrue(u"no_exists_table" in str(ws[0].message))
+        if u"no_exists_table" not in str(ws[0].message):
+            self.fail("'no_exists_table' not in %s" % (str(ws[0].message),))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/tests/test_err.py 
new/PyMySQL-0.7.11/pymysql/tests/test_err.py
--- old/PyMySQL-0.7.6/pymysql/tests/test_err.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/PyMySQL-0.7.11/pymysql/tests/test_err.py        2016-09-03 
21:04:12.000000000 +0200
@@ -0,0 +1,21 @@
+import unittest2
+
+from pymysql import err
+
+
+__all__ = ["TestRaiseException"]
+
+
+class TestRaiseException(unittest2.TestCase):
+
+    def test_raise_mysql_exception(self):
+        data = b"\xff\x15\x04Access denied"
+        with self.assertRaises(err.OperationalError) as cm:
+            err.raise_mysql_exception(data)
+        self.assertEqual(cm.exception.args, (1045, 'Access denied'))
+
+    def test_raise_mysql_exception_client_protocol_41(self):
+        data = b"\xff\x15\x04#28000Access denied"
+        with self.assertRaises(err.OperationalError) as cm:
+            err.raise_mysql_exception(data)
+        self.assertEqual(cm.exception.args, (1045, 'Access denied'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/tests/test_load_local.py 
new/PyMySQL-0.7.11/pymysql/tests/test_load_local.py
--- old/PyMySQL-0.7.6/pymysql/tests/test_load_local.py  2016-07-15 
18:43:37.000000000 +0200
+++ new/PyMySQL-0.7.11/pymysql/tests/test_load_local.py 2016-08-29 
18:14:43.000000000 +0200
@@ -80,7 +80,9 @@
                      "test_load_local FIELDS TERMINATED BY 
','").format(filename)
                 )
                 self.assertEqual(w[0].category, Warning)
-                self.assertTrue("Incorrect integer value" in 
str(w[-1].message))
+                expected_message = "Incorrect integer value"
+                if expected_message not in str(w[-1].message):
+                    self.fail("%r not in %r" % (expected_message, 
w[-1].message))
         finally:
             c.execute("DROP TABLE test_load_local")
             c.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/times.py 
new/PyMySQL-0.7.11/pymysql/times.py
--- old/PyMySQL-0.7.6/pymysql/times.py  2015-09-29 17:10:42.000000000 +0200
+++ new/PyMySQL-0.7.11/pymysql/times.py 2016-08-29 18:14:43.000000000 +0200
@@ -1,16 +1,20 @@
 from time import localtime
 from datetime import date, datetime, time, timedelta
 
+
 Date = date
 Time = time
 TimeDelta = timedelta
 Timestamp = datetime
 
+
 def DateFromTicks(ticks):
     return date(*localtime(ticks)[:3])
 
+
 def TimeFromTicks(ticks):
     return time(*localtime(ticks)[3:6])
 
+
 def TimestampFromTicks(ticks):
     return datetime(*localtime(ticks)[:6])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/pymysql/util.py 
new/PyMySQL-0.7.11/pymysql/util.py
--- old/PyMySQL-0.7.6/pymysql/util.py   2016-07-26 05:09:26.000000000 +0200
+++ new/PyMySQL-0.7.11/pymysql/util.py  2016-08-29 18:14:43.000000000 +0200
@@ -1,14 +1,17 @@
 import struct
 
+
 def byte2int(b):
     if isinstance(b, int):
         return b
     else:
         return struct.unpack("!B", b)[0]
 
+
 def int2byte(i):
     return struct.pack("!B", i)
 
+
 def join_bytes(bs):
     if len(bs) == 0:
         return ""
@@ -17,4 +20,3 @@
         for b in bs[1:]:
             rv += b
         return rv
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/setup.cfg new/PyMySQL-0.7.11/setup.cfg
--- old/PyMySQL-0.7.6/setup.cfg 2016-07-29 05:27:02.000000000 +0200
+++ new/PyMySQL-0.7.11/setup.cfg        2017-04-05 20:11:44.000000000 +0200
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = False
-
 [flake8]
 ignore = E226,E301,E701
 exclude = tests,build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyMySQL-0.7.6/setup.py new/PyMySQL-0.7.11/setup.py
--- old/PyMySQL-0.7.6/setup.py  2016-05-26 06:13:35.000000000 +0200
+++ new/PyMySQL-0.7.11/setup.py 2017-04-05 20:10:50.000000000 +0200
@@ -26,6 +26,7 @@
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Python :: Implementation :: PyPy',
         'Intended Audience :: Developers',


Reply via email to