Skip to content

Commit d42608f

Browse files
committed
Make the Record type available as asyncpg.Record.
Fixes: #93.
1 parent c919573 commit d42608f

File tree

8 files changed

+41
-17
lines changed

8 files changed

+41
-17
lines changed

asyncpg/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from .connection import connect # NOQA
99
from .exceptions import * # NOQA
1010
from .pool import create_pool # NOQA
11+
from .protocol import Record # NOQA
1112
from .types import * # NOQA
1213

1314

14-
__all__ = ('connect', 'create_pool') + exceptions.__all__ # NOQA
15+
__all__ = ('connect', 'create_pool', 'Record') + exceptions.__all__ # NOQA

asyncpg/protocol/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
66

77

8-
from .protocol import Protocol
8+
from .protocol import Protocol, Record # NOQA

asyncpg/protocol/protocol.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -529,4 +529,4 @@ def _create_record(object mapping, tuple elems):
529529
return rec
530530

531531

532-
record.ApgRecord_InitTypes()
532+
Record = <object>record.ApgRecord_InitTypes()

asyncpg/protocol/record/__init__.pxd

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
66

77

8+
cimport cpython
9+
10+
811
cdef extern from "record/recordobj.h":
912

10-
int ApgRecord_InitTypes() except -1
13+
cpython.PyTypeObject *ApgRecord_InitTypes() except NULL
1114

1215
int ApgRecord_CheckExact(object)
1316
object ApgRecord_New(object, int)

asyncpg/protocol/record/recordobj.c

+8-7
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ static PyMethodDef record_methods[] = {
512512

513513
PyTypeObject ApgRecord_Type = {
514514
PyVarObject_HEAD_INIT(NULL, 0)
515-
"Record", /* tp_name */
515+
"asyncpg.Record", /* tp_name */
516516
sizeof(ApgRecordObject) - sizeof(PyObject *), /* tp_basic_size */
517517
sizeof(PyObject *), /* tp_itemsize */
518518
(destructor)record_dealloc, /* tp_dealloc */
@@ -849,25 +849,26 @@ record_new_items_iter(PyObject *seq)
849849
}
850850

851851

852-
int ApgRecord_InitTypes(void)
852+
PyTypeObject *
853+
ApgRecord_InitTypes(void)
853854
{
854855
if (PyType_Ready(&ApgRecord_Type) < 0) {
855-
return -1;
856+
return NULL;
856857
}
857858

858859
if (PyType_Ready(&ApgRecordDesc_Type) < 0) {
859-
return -1;
860+
return NULL;
860861
}
861862

862863
if (PyType_Ready(&ApgRecordIter_Type) < 0) {
863-
return -1;
864+
return NULL;
864865
}
865866

866867
if (PyType_Ready(&ApgRecordItems_Type) < 0) {
867-
return -1;
868+
return NULL;
868869
}
869870

870-
return 0;
871+
return &ApgRecord_Type;
871872
}
872873

873874

asyncpg/protocol/record/recordobj.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ extern PyTypeObject ApgRecordDesc_Type;
4545
#define ApgRecord_GET_ITEM(op, i) \
4646
(((ApgRecordObject *)(op))->ob_item[i])
4747

48-
int ApgRecord_InitTypes(void);
49-
PyObject * ApgRecord_New(PyObject *, Py_ssize_t);
50-
PyObject * ApgRecordDesc_New(PyObject *, PyObject *);
48+
PyTypeObject *ApgRecord_InitTypes(void);
49+
PyObject *ApgRecord_New(PyObject *, Py_ssize_t);
50+
PyObject *ApgRecordDesc_New(PyObject *, PyObject *);
5151

5252
#endif

docs/api/index.rst

+10-3
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ Record Objects
249249
==============
250250

251251
Each row (or composite type value) returned by calls to ``fetch*`` methods
252-
is represented by an instance of the ``Record`` object. ``Record`` objects
253-
are similar to instances of ``collections.namedtuple`` and allow addressing
254-
of values either by a numeric index or by a field name:
252+
is represented by an instance of the :class:`~asyncpg.Record` object.
253+
``Record`` objects are a tuple-/dict-like hybrid, and allow addressing of
254+
items either by a numeric index or by a field name:
255255

256256
.. code-block:: pycon
257257
@@ -267,7 +267,14 @@ of values either by a numeric index or by a field name:
267267
16388
268268
>>> r[0]
269269
16388
270+
>>> dict(r)
271+
{'oid': 16388, 'rolname': 'elvis', 'rolsuper': True}
272+
>>> tuple(r)
273+
(16388, 'elvis', True)
270274
275+
.. note::
276+
277+
``Record`` objects currently cannot be created from Python code.
271278

272279
.. class:: Record()
273280

tests/test_record.py

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pickle
1212
import sys
1313

14+
import asyncpg
1415
from asyncpg import _testbase as tb
1516
from asyncpg.protocol.protocol import _create_record as Record
1617

@@ -297,3 +298,14 @@ async def test_record_duplicate_colnames(self):
297298
self.assertEqual(r['a'], 2)
298299
self.assertEqual(r[0], 1)
299300
self.assertEqual(repr(r), '<Record a=1 a=2>')
301+
302+
async def test_record_isinstance(self):
303+
"""Test that Record works with isinstance."""
304+
r = await self.con.fetchrow('SELECT 1 as a, 2 as b')
305+
self.assertTrue(isinstance(r, asyncpg.Record))
306+
307+
async def test_record_no_new(self):
308+
"""Instances of Record cannot be directly created."""
309+
with self.assertRaisesRegex(
310+
TypeError, "cannot create 'asyncpg.Record' instances"):
311+
asyncpg.Record()

0 commit comments

Comments
 (0)