[Python-modules-commits] [asyncpg] 01/04: Import asyncpg_0.8.4.orig.tar.gz

Piotr Ożarowski piotr at moszumanska.debian.org
Tue Jan 10 19:27:05 UTC 2017


This is an automated email from the git hooks/post-receive script.

piotr pushed a commit to branch master
in repository asyncpg.

commit e9cb3b6f12e56cc396c8f31e98ed2d2f037d9973
Author: Piotr Ożarowski <piotr at debian.org>
Date:   Tue Jan 10 20:19:05 2017 +0100

    Import asyncpg_0.8.4.orig.tar.gz
---
 PKG-INFO                              |     9 +-
 README.rst                            |     7 +-
 asyncpg.egg-info/PKG-INFO             |     9 +-
 asyncpg.egg-info/SOURCES.txt          |     1 +
 asyncpg/introspection.py              |    49 +-
 asyncpg/protocol/buffer.pxd           |    34 +-
 asyncpg/protocol/buffer.pyx           |    46 +-
 asyncpg/protocol/codecs/array.pyx     |   438 +-
 asyncpg/protocol/codecs/base.pxd      |    13 +-
 asyncpg/protocol/codecs/base.pyx      |   174 +-
 asyncpg/protocol/codecs/bits.pyx      |     8 +-
 asyncpg/protocol/codecs/bytea.pyx     |     2 +-
 asyncpg/protocol/codecs/datetime.pyx  |    67 +-
 asyncpg/protocol/codecs/geometry.pyx  |    23 +-
 asyncpg/protocol/codecs/hstore.pyx    |    10 +-
 asyncpg/protocol/codecs/int.pyx       |     6 +-
 asyncpg/protocol/codecs/network.pyx   |     4 +-
 asyncpg/protocol/codecs/range.pyx     |     6 +-
 asyncpg/protocol/codecs/record.pyx    |     9 +-
 asyncpg/protocol/codecs/text.pyx      |     4 +-
 asyncpg/protocol/codecs/textutils.pyx |    72 +
 asyncpg/protocol/codecs/txid.pyx      |     9 +-
 asyncpg/protocol/consts.pxi           |     1 +
 asyncpg/protocol/coreproto.pxd        |     4 +-
 asyncpg/protocol/coreproto.pyx        |     6 +-
 asyncpg/protocol/hton.pxd             |    10 +-
 asyncpg/protocol/pgtypes.pxi          |   136 +-
 asyncpg/protocol/prepared_stmt.pxd    |     2 +-
 asyncpg/protocol/prepared_stmt.pyx    |     4 +-
 asyncpg/protocol/protocol.c           | 18377 +++++++++++++++++++++-----------
 asyncpg/protocol/protocol.pyx         |     6 +-
 asyncpg/protocol/python.pxd           |     5 +
 asyncpg/protocol/record/recordobj.c   |    10 +-
 asyncpg/protocol/settings.pxd         |     2 +-
 asyncpg/protocol/settings.pyx         |    11 +-
 setup.py                              |     4 +-
 tests/test_codecs.py                  |   108 +-
 tests/test_connect.py                 |    17 +-
 tests/test_pool.py                    |     2 +-
 tests/test_timeout.py                 |    25 +-
 40 files changed, 13168 insertions(+), 6562 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 76babe9..eef00cf 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: asyncpg
-Version: 0.8.1
+Version: 0.8.4
 Summary: An asyncio PosgtreSQL driver
 Home-page: https://github.com/MagicStack/asyncpg
 Author: MagicStack Inc
@@ -24,6 +24,9 @@ Description: asyncpg -- A fast PostgreSQL Database Client Library for Python/asy
         framework.  You can read more about asyncpg in an introductory
         `blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
         
+        asyncpg requires Python 3.5 or later and is supported for PostgreSQL
+        versions 9.1 to 9.6.
+        
         
         Documentation
         -------------
@@ -65,8 +68,8 @@ Description: asyncpg -- A fast PostgreSQL Database Client Library for Python/asy
         Installation
         ------------
         
-        asyncpg requires Python 3.5 and is available on PyPI.
-        Use pip to install it::
+        asyncpg is available on PyPI and has no dependencies.
+        Use pip to install::
         
             $ pip install asyncpg
         
diff --git a/README.rst b/README.rst
index c970192..8730e0f 100644
--- a/README.rst
+++ b/README.rst
@@ -16,6 +16,9 @@ of PostgreSQL server binary protocol for use with Python's ``asyncio``
 framework.  You can read more about asyncpg in an introductory
 `blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
 
+asyncpg requires Python 3.5 or later and is supported for PostgreSQL
+versions 9.1 to 9.6.
+
 
 Documentation
 -------------
@@ -57,8 +60,8 @@ This enables asyncpg to have easy-to-use support for:
 Installation
 ------------
 
-asyncpg requires Python 3.5 and is available on PyPI.
-Use pip to install it::
+asyncpg is available on PyPI and has no dependencies.
+Use pip to install::
 
     $ pip install asyncpg
 
diff --git a/asyncpg.egg-info/PKG-INFO b/asyncpg.egg-info/PKG-INFO
index 76babe9..eef00cf 100644
--- a/asyncpg.egg-info/PKG-INFO
+++ b/asyncpg.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: asyncpg
-Version: 0.8.1
+Version: 0.8.4
 Summary: An asyncio PosgtreSQL driver
 Home-page: https://github.com/MagicStack/asyncpg
 Author: MagicStack Inc
@@ -24,6 +24,9 @@ Description: asyncpg -- A fast PostgreSQL Database Client Library for Python/asy
         framework.  You can read more about asyncpg in an introductory
         `blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.
         
+        asyncpg requires Python 3.5 or later and is supported for PostgreSQL
+        versions 9.1 to 9.6.
+        
         
         Documentation
         -------------
@@ -65,8 +68,8 @@ Description: asyncpg -- A fast PostgreSQL Database Client Library for Python/asy
         Installation
         ------------
         
-        asyncpg requires Python 3.5 and is available on PyPI.
-        Use pip to install it::
+        asyncpg is available on PyPI and has no dependencies.
+        Use pip to install::
         
             $ pip install asyncpg
         
diff --git a/asyncpg.egg-info/SOURCES.txt b/asyncpg.egg-info/SOURCES.txt
index 5cc7811..1a18c5c 100644
--- a/asyncpg.egg-info/SOURCES.txt
+++ b/asyncpg.egg-info/SOURCES.txt
@@ -60,6 +60,7 @@ asyncpg/protocol/codecs/numeric.pyx
 asyncpg/protocol/codecs/range.pyx
 asyncpg/protocol/codecs/record.pyx
 asyncpg/protocol/codecs/text.pyx
+asyncpg/protocol/codecs/textutils.pyx
 asyncpg/protocol/codecs/tsearch.pyx
 asyncpg/protocol/codecs/txid.pyx
 asyncpg/protocol/codecs/uuid.pyx
diff --git a/asyncpg/introspection.py b/asyncpg/introspection.py
index 4554629..a9050d9 100644
--- a/asyncpg/introspection.py
+++ b/asyncpg/introspection.py
@@ -7,8 +7,8 @@
 
 INTRO_LOOKUP_TYPES = '''\
 WITH RECURSIVE typeinfo_tree(
-    oid, ns, name, kind, basetype, elemtype, range_subtype,
-    elem_has_bin_input, elem_has_bin_output, attrtypoids, attrnames, depth)
+    oid, ns, name, kind, basetype, has_bin_io, elemtype, elemdelim,
+    range_subtype, elem_has_bin_io, attrtypoids, attrnames, depth)
 AS (
     WITH composite_attrs
     AS (
@@ -58,10 +58,23 @@ AS (
 
                ELSE NULL
             END)                            AS basetype,
+            t.typreceive::oid != 0 AND t.typsend::oid != 0
+                                            AS has_bin_io,
             t.typelem                       AS elemtype,
+            elem_t.typdelim                 AS elemdelim,
             range_t.rngsubtype              AS range_subtype,
-            elem_t.typreceive::oid != 0     AS elem_has_bin_input,
-            elem_t.typsend::oid != 0        AS elem_has_bin_output,
+            (CASE WHEN t.typtype = 'r' THEN
+                (SELECT
+                    range_elem_t.typreceive::oid != 0 AND
+                        range_elem_t.typsend::oid != 0
+                FROM
+                    pg_catalog.pg_type AS range_elem_t
+                WHERE
+                    range_elem_t.oid = range_t.rngsubtype)
+            ELSE
+                elem_t.typreceive::oid != 0 AND
+                    elem_t.typsend::oid != 0
+            END)                            AS elem_has_bin_io,
             (CASE WHEN t.typtype = 'c' THEN
                 (SELECT ca.typoids
                 FROM composite_attrs AS ca
@@ -91,8 +104,8 @@ AS (
     )
 
     SELECT
-        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.elemtype,
-        ti.range_subtype, ti.elem_has_bin_input, ti.elem_has_bin_output,
+        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.has_bin_io,
+        ti.elemtype, ti.elemdelim, ti.range_subtype, ti.elem_has_bin_io,
         ti.attrtypoids, ti.attrnames, 0
     FROM
         typeinfo AS ti
@@ -102,8 +115,8 @@ AS (
     UNION ALL
 
     SELECT
-        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.elemtype,
-        ti.range_subtype, ti.elem_has_bin_input, ti.elem_has_bin_output,
+        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.has_bin_io,
+        ti.elemtype, ti.elemdelim, ti.range_subtype, ti.elem_has_bin_io,
         ti.attrtypoids, ti.attrnames, tt.depth + 1
     FROM
         typeinfo ti,
@@ -126,8 +139,8 @@ ORDER BY
 # Prior to 9.2 PostgreSQL did not have range types.
 INTRO_LOOKUP_TYPES_91 = '''\
 WITH RECURSIVE typeinfo_tree(
-    oid, ns, name, kind, basetype, elemtype, range_subtype,
-    elem_has_bin_input, elem_has_bin_output, attrtypoids, attrnames, depth)
+    oid, ns, name, kind, basetype, has_bin_io, elemtype, elemdelim,
+    range_subtype, elem_has_bin_io, attrtypoids, attrnames, depth)
 AS (
     WITH composite_attrs
     AS (
@@ -177,10 +190,14 @@ AS (
 
                ELSE NULL
             END)                            AS basetype,
+            t.typreceive::oid != 0 AND t.typsend::oid != 0
+                                            AS has_bin_io,
             t.typelem                       AS elemtype,
+            elem_t.typdelim                 AS elemdelim,
             NULL::oid                       AS range_subtype,
-            elem_t.typreceive::oid != 0     AS elem_has_bin_input,
-            elem_t.typsend::oid != 0        AS elem_has_bin_output,
+            elem_t.typreceive::oid != 0 AND
+                elem_t.typsend::oid != 0
+                                            AS elem_has_bin_io,
             (CASE WHEN t.typtype = 'c' THEN
                 (SELECT ca.typoids
                 FROM composite_attrs AS ca
@@ -207,8 +224,8 @@ AS (
     )
 
     SELECT
-        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.elemtype,
-        ti.range_subtype, ti.elem_has_bin_input, ti.elem_has_bin_output,
+        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.has_bin_io,
+        ti.elemtype, ti.elemdelim, ti.range_subtype, ti.elem_has_bin_io,
         ti.attrtypoids, ti.attrnames, 0
     FROM
         typeinfo AS ti
@@ -218,8 +235,8 @@ AS (
     UNION ALL
 
     SELECT
-        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.elemtype,
-        ti.range_subtype, ti.elem_has_bin_input, ti.elem_has_bin_output,
+        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype, ti.has_bin_io,
+        ti.elemtype, ti.elemdelim, ti.range_subtype, ti.elem_has_bin_io,
         ti.attrtypoids, ti.attrnames, tt.depth + 1
     FROM
         typeinfo ti,
diff --git a/asyncpg/protocol/buffer.pxd b/asyncpg/protocol/buffer.pxd
index 2ef7fab..6687e8e 100644
--- a/asyncpg/protocol/buffer.pxd
+++ b/asyncpg/protocol/buffer.pxd
@@ -9,12 +9,12 @@ cdef class Memory:
     cdef:
         char* buf
         object owner
-        int length
+        ssize_t length
 
     cdef as_bytes(self)
 
     @staticmethod
-    cdef inline Memory new(char* buf, object owner, int length)
+    cdef inline Memory new(char* buf, object owner, ssize_t length)
 
 
 cdef class WriteBuffer:
@@ -26,10 +26,10 @@ cdef class WriteBuffer:
         char *_buf
 
         # Allocated size
-        size_t _size
+        ssize_t _size
 
         # Length of data in the buffer
-        size_t _length
+        ssize_t _length
 
         # Number of memoryviews attached to the buffer
         int _view_count
@@ -39,8 +39,8 @@ cdef class WriteBuffer:
 
     cdef inline _check_readonly(self)
     cdef inline len(self)
-    cdef inline _ensure_alloced(self, size_t extra_length)
-    cdef _reallocate(self, new_size)
+    cdef inline _ensure_alloced(self, ssize_t extra_length)
+    cdef _reallocate(self, ssize_t new_size)
     cdef inline start_message(self, char type)
     cdef inline end_message(self)
     cdef write_buffer(self, WriteBuffer buf)
@@ -81,31 +81,31 @@ cdef class ReadBuffer:
         int32_t _bufs_len
 
         # A read position in the first buffer in `_bufs`
-        int32_t _pos0
+        ssize_t _pos0
 
         # Length of the first buffer in `_bufs`
-        int32_t _len0
+        ssize_t _len0
 
         # A total number of buffered bytes in ReadBuffer
-        int32_t _length
+        ssize_t _length
 
         char _current_message_type
         int _current_message_len
-        int32_t _current_message_len_unread
+        ssize_t _current_message_len_unread
         bint _current_message_ready
 
     cdef feed_data(self, data)
     cdef inline _ensure_first_buf(self)
     cdef _switch_to_next_buf(self)
     cdef inline read_byte(self)
-    cdef inline char* _try_read_bytes(self, int nbytes)
-    cdef inline read(self, int nbytes)
+    cdef inline char* _try_read_bytes(self, ssize_t nbytes)
+    cdef inline read(self, ssize_t nbytes)
     cdef inline read_bytes(self, ssize_t n)
     cdef inline read_int32(self)
     cdef inline read_int16(self)
     cdef inline read_cstr(self)
     cdef int32_t has_message(self) except -1
-    cdef inline char* try_consume_message(self, int32_t* len)
+    cdef inline char* try_consume_message(self, ssize_t* len)
     cdef Memory consume_message(self)
     cdef discard_message(self)
     cdef inline _discard_message(self)
@@ -119,13 +119,13 @@ cdef class ReadBuffer:
 cdef class FastReadBuffer:
     cdef:
         const char* buf
-        size_t len
+        ssize_t len
 
-    cdef inline const char* read(self, size_t n) except NULL
+    cdef inline const char* read(self, ssize_t n) except NULL
     cdef inline const char* read_all(self)
     cdef inline FastReadBuffer slice_from(self, FastReadBuffer source,
-                                          size_t len)
-    cdef _raise_ins_err(self, size_t n, size_t len)
+                                          ssize_t len)
+    cdef _raise_ins_err(self, ssize_t n, ssize_t len)
 
     @staticmethod
     cdef FastReadBuffer new()
diff --git a/asyncpg/protocol/buffer.pyx b/asyncpg/protocol/buffer.pyx
index 1649ec9..e999b7a 100644
--- a/asyncpg/protocol/buffer.pyx
+++ b/asyncpg/protocol/buffer.pyx
@@ -21,7 +21,7 @@ cdef class Memory:
         return cpython.PyBytes_FromStringAndSize(self.buf, self.length)
 
     @staticmethod
-    cdef inline Memory new(char* buf, object owner, int length):
+    cdef inline Memory new(char* buf, object owner, ssize_t length):
         cdef Memory mem
         mem = Memory.__new__(Memory)
         mem.buf = buf
@@ -70,13 +70,13 @@ cdef class WriteBuffer:
     cdef inline len(self):
         return self._length
 
-    cdef inline _ensure_alloced(self, size_t extra_length):
-        cdef size_t new_size = extra_length + self._length
+    cdef inline _ensure_alloced(self, ssize_t extra_length):
+        cdef ssize_t new_size = extra_length + self._length
 
         if new_size > self._size:
             self._reallocate(new_size)
 
-    cdef _reallocate(self, new_size):
+    cdef _reallocate(self, ssize_t new_size):
         cdef char *new_buf
 
         if new_size < _BUFFER_MAX_GROW:
@@ -86,18 +86,18 @@ cdef class WriteBuffer:
             new_size += _BUFFER_INITIAL_SIZE
 
         if self._smallbuf_inuse:
-            new_buf = <char*>PyMem_Malloc(sizeof(char) * new_size)
+            new_buf = <char*>PyMem_Malloc(sizeof(char) * <size_t>new_size)
             if new_buf is NULL:
                 self._buf = NULL
                 self._size = 0
                 self._length = 0
                 raise MemoryError
-            memcpy(new_buf, self._buf, self._size)
+            memcpy(new_buf, self._buf, <size_t>self._size)
             self._size = new_size
             self._buf = new_buf
             self._smallbuf_inuse = False
         else:
-            new_buf = <char*>PyMem_Realloc(<void*>self._buf, new_size)
+            new_buf = <char*>PyMem_Realloc(<void*>self._buf, <size_t>new_size)
             if new_buf is NULL:
                 PyMem_Free(self._buf)
                 self._buf = NULL
@@ -117,7 +117,7 @@ cdef class WriteBuffer:
 
     cdef inline end_message(self):
         # "length-1" to exclude the message type byte
-        cdef size_t mlen = self._length - 1
+        cdef ssize_t mlen = self._length - 1
 
         self._check_readonly()
         if not self._message_mode:
@@ -125,8 +125,10 @@ cdef class WriteBuffer:
                 'end_message can only be called with start_message')
         if self._length < 5:
             raise BufferError('end_message: buffer is too small')
+        if mlen > _MAXINT32:
+            raise BufferError('end_message: message is too large')
 
-        hton.pack_int32(&self._buf[1], mlen)
+        hton.pack_int32(&self._buf[1], <int32_t>mlen)
         return self
 
     cdef write_buffer(self, WriteBuffer buf):
@@ -138,7 +140,7 @@ cdef class WriteBuffer:
         self._ensure_alloced(buf._length)
         memcpy(self._buf + self._length,
                <void*>buf._buf,
-               buf._length)
+               <size_t>buf._length)
         self._length += buf._length
 
     cdef write_byte(self, char b):
@@ -171,7 +173,7 @@ cdef class WriteBuffer:
         self._check_readonly()
         self._ensure_alloced(len)
 
-        memcpy(self._buf + self._length, <void*>data, len)
+        memcpy(self._buf + self._length, <void*>data, <size_t>len)
         self._length += len
 
     cdef write_int16(self, int16_t i):
@@ -246,7 +248,7 @@ cdef class ReadBuffer:
 
     cdef feed_data(self, data):
         cdef:
-            int32_t dlen
+            ssize_t dlen
             bytes data_bytes
 
         if not cpython.PyBytes_CheckExact(data):
@@ -293,7 +295,7 @@ cdef class ReadBuffer:
                 raise RuntimeError(
                     'debug: second buffer of ReadBuffer is empty')
 
-    cdef inline char* _try_read_bytes(self, int nbytes):
+    cdef inline char* _try_read_bytes(self, ssize_t nbytes):
         # Important: caller must call _ensure_first_buf() prior
         # to calling try_read_bytes, and must not overread
 
@@ -319,10 +321,10 @@ cdef class ReadBuffer:
         else:
             return NULL
 
-    cdef inline read(self, int nbytes):
+    cdef inline read(self, ssize_t nbytes):
         cdef:
             object result
-            int nread
+            ssize_t nread
             char *buf
 
         self._ensure_first_buf()
@@ -419,8 +421,8 @@ cdef class ReadBuffer:
                 'to be in the buffer')
 
         cdef:
-            int pos
-            int nread
+            ssize_t pos
+            ssize_t nread
             bytes result
             char* buf
             char* buf_start
@@ -504,8 +506,8 @@ cdef class ReadBuffer:
         self._current_message_ready = 1
         return 1
 
-    cdef inline char* try_consume_message(self, int32_t* len):
-        cdef int32_t buf_len
+    cdef inline char* try_consume_message(self, ssize_t* len):
+        cdef ssize_t buf_len
 
         if not self._current_message_ready:
             return NULL
@@ -579,7 +581,7 @@ cdef class ReadBuffer:
 @cython.freelist(_BUFFER_FREELIST_SIZE)
 cdef class FastReadBuffer:
 
-    cdef inline const char* read(self, size_t n) except NULL:
+    cdef inline const char* read(self, ssize_t n) except NULL:
         cdef const char *result
 
         if n > self.len:
@@ -599,12 +601,12 @@ cdef class FastReadBuffer:
         return result
 
     cdef inline FastReadBuffer slice_from(self, FastReadBuffer source,
-                                          size_t len):
+                                          ssize_t len):
         self.buf = source.read(len)
         self.len = len
         return self
 
-    cdef _raise_ins_err(self, size_t n, size_t len):
+    cdef _raise_ins_err(self, ssize_t n, ssize_t len):
         raise BufferError(
             'insufficient data in buffer: requested {}, remaining {}'.
                 format(n, self.len))
diff --git a/asyncpg/protocol/codecs/array.pyx b/asyncpg/protocol/codecs/array.pyx
index 92706c4..5c13b2e 100644
--- a/asyncpg/protocol/codecs/array.pyx
+++ b/asyncpg/protocol/codecs/array.pyx
@@ -10,6 +10,9 @@ from collections.abc import Container as ContainerABC
 
 DEF ARRAY_MAXDIM = 6  # defined in postgresql/src/includes/c.h
 
+# "NULL"
+cdef Py_UCS4 *APG_NULL = [0x004E, 0x0055, 0x004C, 0x004C, 0x0000]
+
 
 ctypedef object (*encode_func_ex)(ConnectionSettings settings,
                                   WriteBuffer buf,
@@ -38,21 +41,26 @@ cdef inline _is_sub_array(object obj):
 
 cdef _get_array_shape(object obj, int32_t *dims, int32_t *ndims):
     cdef:
-        int32_t mylen = len(obj)
-        int32_t elemlen = -2
+        ssize_t mylen = len(obj)
+        ssize_t elemlen = -2
         object it
 
+    if mylen > _MAXINT32:
+        raise ValueError('too many elements in array value')
+
     if ndims[0] > ARRAY_MAXDIM:
         raise ValueError(
             'number of array dimensions ({}) exceed the maximum expected ({})'.
                 format(ndims[0], ARRAY_MAXDIM))
 
-    dims[ndims[0] - 1] = mylen
+    dims[ndims[0] - 1] = <int32_t>mylen
 
     for elem in obj:
         if _is_sub_array(elem):
             if elemlen == -2:
                 elemlen = len(elem)
+                if elemlen > _MAXINT32:
+                    raise ValueError('too many elements in array value')
                 ndims[0] += 1
                 _get_array_shape(elem, dims, ndims)
             else:
@@ -123,7 +131,7 @@ cdef inline array_encode(ConnectionSettings settings, WriteBuffer buf,
     # flags
     buf.write_int32(0)
     # element type
-    buf.write_int32(elem_oid)
+    buf.write_int32(<int32_t>elem_oid)
     # upper / lower bounds
     for i in range(ndims):
         buf.write_int32(dims[i])
@@ -137,11 +145,11 @@ cdef inline array_decode(ConnectionSettings settings, FastReadBuffer buf,
     cdef:
         int32_t ndims = hton.unpack_int32(buf.read(4))
         int32_t flags = hton.unpack_int32(buf.read(4))
-        uint32_t elem_oid = hton.unpack_int32(buf.read(4))
+        uint32_t elem_oid = <uint32_t>hton.unpack_int32(buf.read(4))
         list result
-        uint32_t i
+        int i
         int32_t elem_len
-        int64_t elem_count = 1
+        int32_t elem_count = 1
         FastReadBuffer elem_buf = FastReadBuffer.new()
         int32_t dims[ARRAY_MAXDIM]
         Codec elem_codec
@@ -152,8 +160,8 @@ cdef inline array_decode(ConnectionSettings settings, FastReadBuffer buf,
 
     if ndims > ARRAY_MAXDIM:
         raise RuntimeError(
-            'number of array dimensions exceed the maximum expected ({})'.
-            format(ARRAY_MAXDIM))
+            'number of array dimensions ({}) exceed the maximum expected ({})'.
+            format(ndims, ARRAY_MAXDIM))
 
     if decoder == NULL:
         # No decoder is known beforehand, look it up
@@ -168,12 +176,12 @@ cdef inline array_decode(ConnectionSettings settings, FastReadBuffer buf,
 
     for i in range(ndims):
         dims[i] = hton.unpack_int32(buf.read(4))
-        elem_count *= dims[i]
         # Ignore the lower bound information
         buf.read(4)
 
     if ndims == 1:
         # Fast path for flat arrays
+        elem_count = dims[0]
         result = cpython.PyList_New(elem_count)
 
         for i in range(elem_count):
@@ -307,6 +315,416 @@ cdef inline _nested_array_decode(ConnectionSettings settings,
     return result
 
 
+cdef textarray_decode(ConnectionSettings settings, FastReadBuffer buf,
+                      decode_func_ex decoder, const void *decoder_arg,
+                      Py_UCS4 typdelim):
+    cdef:
+        Py_UCS4 *array_text
+        str s
+
+    # Make a copy of array data since we will be mutating it for
+    # the purposes of element decoding.
+    s = text_decode(settings, buf)
+    array_text = PyUnicode_AsUCS4Copy(s)
+
+    try:
+        return _textarray_decode(
+            settings, array_text, decoder, decoder_arg, typdelim)
+    except ValueError as e:
+        raise ValueError(
+            'malformed array literal {!r}: {}'.format(s, e.args[0]))
+    finally:
+        PyMem_Free(array_text)
+
+
+cdef _textarray_decode(ConnectionSettings settings,
+                       Py_UCS4 *array_text,
+                       decode_func_ex decoder,
+                       const void *decoder_arg,
+                       Py_UCS4 typdelim):
+
+    cdef:
+        bytearray array_bytes
+        list result
+        list new_stride
+        Py_UCS4 *ptr
+        int32_t ndims = 0
+        int32_t ubound = 0
+        int32_t lbound = 0
+        int32_t dims[ARRAY_MAXDIM]
+        int32_t inferred_dims[ARRAY_MAXDIM]
+        int32_t inferred_ndims = 0
+        void *strides[ARRAY_MAXDIM]
+        int32_t indexes[ARRAY_MAXDIM]
+        int32_t nest_level = 0
+        int32_t item_level = 0
+        bint end_of_array = False
+
+        bint end_of_item = False
+        bint has_quoting = False
+        bint strip_spaces = False
+        bint in_quotes = False
+        Py_UCS4 *item_start
+        Py_UCS4 *item_ptr
+        Py_UCS4 *item_end
+
+        int i
+        object item
+        str item_text
+        FastReadBuffer item_buf = FastReadBuffer.new()
+        char *pg_item_str
+        ssize_t pg_item_len
+
+    ptr = array_text
+
+    while True:
+        while apg_ascii_isspace(ptr[0]):
+            ptr += 1
+
+        if ptr[0] != '[':
+            # Finished parsing dimensions spec.
+            break
+
+        ptr += 1  # '['
+
+        if ndims > ARRAY_MAXDIM:
+            raise ValueError(
+                'number of array dimensions ({}) exceed the '
+                'maximum expected ({})'.format(ndims, ARRAY_MAXDIM))
+
+        ptr = apg_parse_int32(ptr, &ubound)
+        if ptr == NULL:
+            raise ValueError('missing array dimension value')
+
+        if ptr[0] == ':':
+            ptr += 1
+            lbound = ubound
+
+            # [lower:upper] spec.  We disregard the lbound for decoding.
+            ptr = apg_parse_int32(ptr, &ubound)
+            if ptr == NULL:
+                raise ValueError('missing array dimension value')
+        else:
+            lbound = 1
+
+        if ptr[0] != ']':
+            raise ValueError('missing \']\' after array dimensions')
+
+        ptr += 1  # ']'
+
+        dims[ndims] = ubound - lbound + 1
+        ndims += 1
+
+    if ndims != 0:
+        # If dimensions were given, the '=' token is expected.
+        if ptr[0] != '=':
+            raise ValueError('missing \'=\' after array dimensions')
+
+        ptr += 1  # '='
+
+        # Skip any whitespace after the '=', whitespace
+        # before was consumed in the above loop.
+        while apg_ascii_isspace(ptr[0]):
+            ptr += 1
+
+        # Infer the dimensions from the brace structure in the
+        # array literal body, and check that it matches the explicit
+        # spec.  This also validates that the array literal is sane.
+        _infer_array_dims(ptr, typdelim, inferred_dims, &inferred_ndims)
+
+        if inferred_ndims != ndims:
+            raise ValueError(
+                'specified array dimensions do not match array content')
+
+        for i in range(ndims):
+            if inferred_dims[i] != dims[i]:
+                raise ValueError(
+                    'specified array dimensions do not match array content')
+    else:
+        # Infer the dimensions from the brace structure in the array literal
+        # body.  This also validates that the array literal is sane.
+        _infer_array_dims(ptr, typdelim, dims, &ndims)
+
+    while not end_of_array:
+        # We iterate over the literal character by character
+        # and modify the string in-place removing the array-specific
+        # quoting and determining the boundaries of each element.
+        end_of_item = has_quoting = in_quotes = False
+        strip_spaces = True
+
+        # Pointers to array element start, end, and the current pointer
+        # tracking the position where characters are written when
+        # escaping is folded.
+        item_start = item_end = item_ptr = ptr
+        item_level = 0
+
+        while not end_of_item:
+            if ptr[0] == '"':
+                in_quotes = not in_quotes
+                if in_quotes:
+                    strip_spaces = False
+                else:
+                    item_end = item_ptr
+                has_quoting = True
+
+            elif ptr[0] == '\\':
+                # Quoted character, collapse the backslash.
+                ptr += 1
+                has_quoting = True
+                item_ptr[0] = ptr[0]
+                item_ptr += 1
+                strip_spaces = False
+                item_end = item_ptr
+
+            elif in_quotes:
+                # Consume the string until we see the closing quote.
+                item_ptr[0] = ptr[0]
+                item_ptr += 1
+
+            elif ptr[0] == '{':
+                # Nesting level increase.
+                nest_level += 1
+
+                indexes[nest_level - 1] = 0
+                new_stride = cpython.PyList_New(dims[nest_level - 1])
+                strides[nest_level - 1] = \
+                    <void*>(<cpython.PyObject>new_stride)
+
+                if nest_level > 1:
+                    cpython.Py_INCREF(new_stride)
+                    cpython.PyList_SET_ITEM(
+                        <object><cpython.PyObject*>strides[nest_level - 2],
+                        indexes[nest_level - 2],
+                        new_stride)
+                else:
+                    result = new_stride
+
+            elif ptr[0] == '}':
+                if item_level == 0:
+                    # Make sure we keep track of which nesting
+                    # level the item belongs to, as the loop
+                    # will continue to consume closing braces
+                    # until the delimiter or the end of input.
+                    item_level = nest_level
+
+                nest_level -= 1
+
+                if nest_level == 0:
+                    end_of_array = end_of_item = True
+
+            elif ptr[0] == typdelim:
+                # Array element delimiter,
+                end_of_item = True
+                if item_level == 0:
+                    item_level = nest_level
+
+            elif apg_ascii_isspace(ptr[0]):
+                if not strip_spaces:
+                    item_ptr[0] = ptr[0]
+                    item_ptr += 1
+                # Ignore the leading literal whitespace.
+
+            else:
+                item_ptr[0] = ptr[0]
+                item_ptr += 1
+                strip_spaces = False
+                item_end = item_ptr
+
+            ptr += 1
+
+        # end while not end_of_item
+
+        if item_end == item_start:
+            # Empty array
+            continue
+
+        item_end[0] = '\0'
+
+        if not has_quoting and apg_strcasecmp(item_start, APG_NULL) == 0:
+            # NULL element.
+            item = None
+        else:
+            # XXX: find a way to avoid the redundant encode/decode
+            # cycle here.
+            item_text = PyUnicode_FromKindAndData(
+                PyUnicode_4BYTE_KIND,
+                <void *>item_start,
+                item_end - item_start)
+
+            # Prepare the element buffer and call the text decoder
+            # for the element type.
+            as_pg_string_and_size(
+                settings, item_text, &pg_item_str, &pg_item_len)
+            item_buf.buf = pg_item_str
+            item_buf.len = pg_item_len
+            item = decoder(settings, item_buf, decoder_arg)
+
+        # Place the decoded element in the array.
+        cpython.Py_INCREF(item)
+        cpython.PyList_SET_ITEM(
+            <object><cpython.PyObject*>strides[item_level - 1],
+            indexes[item_level - 1],
+            item)
+
+        indexes[nest_level - 1] += 1
+
+    return result
+
+
+cdef enum _ArrayParseState:
+    APS_START = 1
+    APS_STRIDE_STARTED = 2
+    APS_STRIDE_DONE = 3
+    APS_STRIDE_DELIMITED = 4
+    APS_ELEM_STARTED = 5
+    APS_ELEM_DELIMITED = 6
+
+
+cdef _UnexpectedCharacter(const Py_UCS4 *array_text, const Py_UCS4 *ptr):
+    return ValueError('unexpected character {!r} at position {}'.format(
+        cpython.PyUnicode_FromOrdinal(<int>ptr[0]), ptr - array_text + 1))
+
+
+cdef _infer_array_dims(const Py_UCS4 *array_text,
+                       Py_UCS4 typdelim,
+                       int32_t *dims,
+                       int32_t *ndims):
+    cdef:
+        const Py_UCS4 *ptr = array_text
+        int i
+        int nest_level = 0
+        bint end_of_array = False
+        bint end_of_item = False
+        bint in_quotes = False
+        bint array_is_empty = True
+        int stride_len[ARRAY_MAXDIM]
+        int prev_stride_len[ARRAY_MAXDIM]
+        _ArrayParseState parse_state = APS_START
+
+    for i in range(ARRAY_MAXDIM):
+        dims[i] = prev_stride_len[i] = 0
+        stride_len[i] = 1
+
+    while not end_of_array:
+        end_of_item = False
+
+        while not end_of_item:
+            if ptr[0] == '\0':
+                raise ValueError('unexpected end of string')
+
+            elif ptr[0] == '"':
+                if (parse_state not in (APS_STRIDE_STARTED,
+                                        APS_ELEM_DELIMITED) and
+                        not (parse_state == APS_ELEM_STARTED and in_quotes)):
+                    raise _UnexpectedCharacter(array_text, ptr)
+
+                in_quotes = not in_quotes
+                if in_quotes:
+                    parse_state = APS_ELEM_STARTED
+                    array_is_empty = False
+
+            elif ptr[0] == '\\':
+                if parse_state not in (APS_STRIDE_STARTED,
+                                       APS_ELEM_STARTED,
+                                       APS_ELEM_DELIMITED):
+                    raise _UnexpectedCharacter(array_text, ptr)
+
+                parse_state = APS_ELEM_STARTED
+                array_is_empty = False
+
+                if ptr[1] != '\0':
+                    ptr += 1
+                else:
+                    raise ValueError('unexpected end of string')
+
+            elif in_quotes:
+                # Ignore everything inside the quotes.
+                pass
+
+            elif ptr[0] == '{':
+                if parse_state not in (APS_START,
+                                       APS_STRIDE_STARTED,
+                                       APS_STRIDE_DELIMITED):
+                    raise _UnexpectedCharacter(array_text, ptr)
+
+                parse_state = APS_STRIDE_STARTED
+                if nest_level >= ARRAY_MAXDIM:
+                    raise ValueError(
+                        'number of array dimensions ({}) exceed the '
+                        'maximum expected ({})'.format(
+                            nest_level, ARRAY_MAXDIM))
+
+                dims[nest_level] = 0
+                nest_level += 1
+                if ndims[0] < nest_level:
+                    ndims[0] = nest_level
+
+            elif ptr[0] == '}':
+                if (parse_state not in (APS_ELEM_STARTED, APS_STRIDE_DONE) and
+                        not (nest_level == 1 and
+                             parse_state == APS_STRIDE_STARTED)):
+                    raise _UnexpectedCharacter(array_text, ptr)
+
+                parse_state = APS_STRIDE_DONE
+
+                if nest_level == 0:
+                    raise _UnexpectedCharacter(array_text, ptr)
+
+                nest_level -= 1
+
+                if (prev_stride_len[nest_level] != 0 and
+                        stride_len[nest_level] != prev_stride_len[nest_level]):
+                    raise ValueError(
+                        'inconsistent sub-array dimensions'
+                        ' at position {}'.format(
+                            ptr - array_text + 1))
+
+                prev_stride_len[nest_level] = stride_len[nest_level]
+                stride_len[nest_level] = 1
+                if nest_level == 0:
+                    end_of_array = end_of_item = True
+                else:
+                    dims[nest_level - 1] += 1
+
... 40877 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/asyncpg.git



More information about the Python-modules-commits mailing list