Skip to content

Commit c1de1fd

Browse files
committed
Exporting features to Connection class
1 parent 2277115 commit c1de1fd

File tree

10 files changed

+148
-8
lines changed

10 files changed

+148
-8
lines changed

.github/workflows/actions.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ jobs:
88
matrix:
99
os: [ ubuntu-latest, macos-latest ]
1010
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.10']
11-
tarantool: ['1.10', '2']
11+
tarantool: ['1.10', '2', '3']
1212
exclude:
1313
- os: macos-latest
1414
tarantool: '1.10'
15+
- os: macos-latest
16+
tarantool: '3'
1517
- python-version: 'pypy3.10'
1618
tarantool: '1.10'
19+
- python-version: 'pypy3.10'
20+
tarantool: '3'
1721

1822
runs-on: ${{ matrix.os }}
1923

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,5 @@ deploy_key*
111111
!.ci/deploy_key.enc
112112
/core
113113
cython_debug
114+
115+
temp

asynctnt/connection.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ async def reconnect(self):
444444
await self.disconnect()
445445
await self.connect()
446446

447-
async def __aenter__(self):
447+
async def __aenter__(self) -> "Connection":
448448
"""
449449
Executed on entering the async with section.
450450
Connects to Tarantool instance.
@@ -606,7 +606,7 @@ def _normalize_api(self):
606606
Api.call = Api.call16
607607
Connection.call = Connection.call16
608608

609-
if self.version < (2, 10): # pragma: nocover
609+
if not self.features.streams: # pragma: nocover
610610

611611
def stream_stub(_):
612612
raise TarantoolError("streams are available only in Tarantool 2.10+")
@@ -627,6 +627,14 @@ def stream(self) -> Stream:
627627
stream._set_db(db)
628628
return stream
629629

630+
@property
631+
def features(self) -> protocol.IProtoFeatures:
632+
"""
633+
Lookup available Tarantool features - https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_iproto/feature/
634+
:return:
635+
"""
636+
return self._protocol.features
637+
630638

631639
async def connect(**kwargs) -> Connection:
632640
"""

asynctnt/iproto/protocol.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ cdef class BaseProtocol(CoreProtocol):
6969
bint _schema_fetch_in_progress
7070
object _refetch_schema_future
7171
Db _db
72+
IProtoFeatures _features
7273
req_execute_func execute
7374

7475
object create_future

asynctnt/iproto/protocol.pyi

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,23 @@ class Protocol:
170170
def schema_id(self) -> int: ...
171171
@property
172172
def schema(self) -> Schema: ...
173+
@property
174+
def features(self) -> IProtoFeatures: ...
173175
def create_db(self, gen_stream_id: bool = False) -> Db: ...
174176
def get_common_db(self) -> Db: ...
175177
def refetch_schema(self) -> asyncio.Future: ...
176178
def is_connected(self) -> bool: ...
177179
def is_fully_connected(self) -> bool: ...
178180
def get_version(self) -> tuple: ...
181+
182+
class IProtoFeatures:
183+
streams: bool
184+
transactions: bool
185+
error_extension: bool
186+
watchers: bool
187+
pagination: bool
188+
space_and_index_names: bool
189+
watch_once: bool
190+
dml_tuple_extension: bool
191+
call_ret_tuple_extension: bool
192+
call_arg_tuple_extension: bool

asynctnt/iproto/protocol.pyx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ cdef class BaseProtocol(CoreProtocol):
9898
self._schema_fetch_in_progress = False
9999
self._refetch_schema_future = None
100100
self._db = self._create_db(<bint> False)
101+
self._features = IProtoFeatures.__new__(IProtoFeatures)
101102
self.execute = self._execute_bad
102103

103104
try:
@@ -253,9 +254,7 @@ cdef class BaseProtocol(CoreProtocol):
253254
return
254255
e = f.exception()
255256
if not e:
256-
logger.debug('Tarantool[%s:%s] identified successfully',
257-
self.host, self.port)
258-
257+
self._features = (<Response> f.result()).result_
259258
self.post_con_state = POST_CONNECTION_AUTH
260259
self._post_con_state_machine()
261260
else:
@@ -515,6 +514,10 @@ cdef class BaseProtocol(CoreProtocol):
515514
def refetch_schema(self):
516515
return self._refetch_schema()
517516

517+
@property
518+
def features(self) -> IProtoFeatures:
519+
return self._features
520+
518521

519522
class Protocol(BaseProtocol, asyncio.Protocol):
520523
pass

asynctnt/iproto/response.pxd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ cdef class Response:
4141
bint _push_subscribe
4242
BaseRequest request_
4343
object _exception
44+
object result_
4445

4546
readonly object _q
4647
readonly object _push_event
@@ -66,3 +67,16 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
6667
Response resp, BaseRequest req,
6768
bint is_chunk) except -1
6869
cdef IProtoError parse_iproto_error(const char ** b, bytes encoding)
70+
71+
cdef class IProtoFeatures:
72+
cdef:
73+
readonly bint streams
74+
readonly bint transactions
75+
readonly bint error_extension
76+
readonly bint watchers
77+
readonly bint pagination
78+
readonly bint space_and_index_names
79+
readonly bint watch_once
80+
readonly bint dml_tuple_extension
81+
readonly bint call_ret_tuple_extension
82+
readonly bint call_arg_tuple_extension

asynctnt/iproto/response.pyx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ cdef class IProtoErrorStackFrame:
2525
cdef class IProtoError:
2626
pass
2727

28+
@cython.final
29+
cdef class IProtoFeatures:
30+
def __repr__(self):
31+
return (f"<IProtoFeatures"
32+
f" streams={self.streams}"
33+
f" transactions={self.transactions}"
34+
f" error_extension={self.error_extension}"
35+
f" watchers={self.watchers}"
36+
f" pagination={self.pagination}"
37+
f" space_and_index_names={self.space_and_index_names}"
38+
f" watch_once={self.watch_once}"
39+
f" dml_tuple_extension={self.dml_tuple_extension}"
40+
f" call_ret_tuple_extension={self.call_ret_tuple_extension}"
41+
f" call_arg_tuple_extension={self.call_arg_tuple_extension}"
42+
f">"
43+
)
44+
2845
@cython.final
2946
@cython.freelist(REQUEST_FREELIST)
3047
cdef class Response:
@@ -41,6 +58,7 @@ cdef class Response:
4158
self.errmsg = None
4259
self.error = None
4360
self._rowcount = 0
61+
self.result_ = None
4462
self.body = None
4563
self.encoding = None
4664
self.metadata = None
@@ -546,6 +564,7 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
546564
const char *s
547565
list data
548566
Field field
567+
IProtoFeatures features
549568

550569
b = <const char *> buf
551570
# mp_fprint(stdio.stdout, b)
@@ -635,7 +654,33 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
635654
logger.debug("IProto version: %s", _decode_obj(&b, resp.encoding))
636655

637656
elif key == tarantool.IPROTO_FEATURES:
638-
logger.debug("IProto features available: %s", _decode_obj(&b, resp.encoding))
657+
features = <IProtoFeatures> IProtoFeatures.__new__(IProtoFeatures)
658+
659+
for item in _decode_obj(&b, resp.encoding):
660+
if item == 0:
661+
features.streams = 1
662+
elif item == 1:
663+
features.transactions = 1
664+
elif item == 2:
665+
features.error_extension = 1
666+
elif item == 3:
667+
features.watchers = 1
668+
elif item == 4:
669+
features.pagination = 1
670+
elif item == 5:
671+
features.space_and_index_names = 1
672+
elif item == 6:
673+
features.watch_once = 1
674+
elif item == 7:
675+
features.dml_tuple_extension = 1
676+
elif item == 8:
677+
features.call_ret_tuple_extension = 1
678+
elif item == 9:
679+
features.call_arg_tuple_extension = 1
680+
else:
681+
logger.debug("unknown iproto feature available: %d", item)
682+
683+
resp.result_ = features
639684

640685
elif key == tarantool.IPROTO_AUTH_TYPE:
641686
logger.debug("IProto auth type: %s", _decode_obj(&b, resp.encoding))

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def initialize_options(self):
4242
self.debug = True
4343
self.gdb_debug = True
4444
else:
45-
self.cython_always = False
45+
self.cython_always = True
4646
self.cython_annotate = None
4747
self.cython_directives = None
4848
self.gdb_debug = False

tests/test_connect.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,52 @@ async def state_checker():
802802
await conn.call("box.info")
803803
finally:
804804
await conn.disconnect()
805+
806+
async def test__features(self):
807+
async with asynctnt.Connection(host=self.tnt.host, port=self.tnt.port) as conn:
808+
if not check_version(
809+
self,
810+
conn.version,
811+
min=(2, 10),
812+
max=(3, 0),
813+
min_included=True,
814+
max_included=False,
815+
):
816+
return
817+
818+
self.assertIsNotNone(conn.features)
819+
self.assertTrue(conn.features.streams)
820+
self.assertTrue(conn.features.watchers)
821+
self.assertTrue(conn.features.error_extension)
822+
self.assertTrue(conn.features.transactions)
823+
self.assertTrue(conn.features.pagination)
824+
825+
self.assertFalse(conn.features.space_and_index_names)
826+
self.assertFalse(conn.features.watch_once)
827+
self.assertFalse(conn.features.dml_tuple_extension)
828+
self.assertFalse(conn.features.call_ret_tuple_extension)
829+
self.assertFalse(conn.features.call_arg_tuple_extension)
830+
831+
async def test__features_3_0(self):
832+
async with asynctnt.Connection(host=self.tnt.host, port=self.tnt.port) as conn:
833+
if not check_version(
834+
self,
835+
conn.version,
836+
min=(3, 0),
837+
min_included=True,
838+
max_included=False,
839+
):
840+
return
841+
842+
self.assertIsNotNone(conn.features)
843+
self.assertTrue(conn.features.streams)
844+
self.assertTrue(conn.features.watchers)
845+
self.assertTrue(conn.features.error_extension)
846+
self.assertTrue(conn.features.transactions)
847+
self.assertTrue(conn.features.pagination)
848+
849+
self.assertTrue(conn.features.space_and_index_names)
850+
self.assertTrue(conn.features.watch_once)
851+
self.assertTrue(conn.features.dml_tuple_extension)
852+
self.assertTrue(conn.features.call_ret_tuple_extension)
853+
self.assertTrue(conn.features.call_arg_tuple_extension)

0 commit comments

Comments
 (0)