Skip to content

Commit

Permalink
Exporting features to Connection class
Browse files Browse the repository at this point in the history
  • Loading branch information
igorcoding committed May 9, 2024
1 parent 87ba91c commit 495892f
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,5 @@ deploy_key*
!.ci/deploy_key.enc
/core
cython_debug

temp
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v2.3.0
**New features:**
* Added support for [interval types](https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/interval_object/) [#30](https://github.com/igorcoding/asynctnt/issues/30)
* Added ability to retrieve IProto features [available](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_iproto/feature/) in Tarantool using `conn.features` property


## v2.2.0
Expand Down
12 changes: 10 additions & 2 deletions asynctnt/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ async def reconnect(self):
await self.disconnect()
await self.connect()

async def __aenter__(self):
async def __aenter__(self) -> "Connection":
"""
Executed on entering the async with section.
Connects to Tarantool instance.
Expand Down Expand Up @@ -606,7 +606,7 @@ def _normalize_api(self):
Api.call = Api.call16
Connection.call = Connection.call16

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

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

@property
def features(self) -> protocol.IProtoFeatures:
"""
Lookup available Tarantool features - https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_iproto/feature/
:return:
"""
return self._protocol.features


async def connect(**kwargs) -> Connection:
"""
Expand Down
1 change: 1 addition & 0 deletions asynctnt/iproto/protocol.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ cdef class BaseProtocol(CoreProtocol):
bint _schema_fetch_in_progress
object _refetch_schema_future
Db _db
IProtoFeatures _features
req_execute_func execute

object create_future
Expand Down
16 changes: 16 additions & 0 deletions asynctnt/iproto/protocol.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,16 @@ class Protocol:
def schema_id(self) -> int: ...
@property
def schema(self) -> Schema: ...
@property
def features(self) -> IProtoFeatures: ...
def create_db(self, gen_stream_id: bool = False) -> Db: ...
def get_common_db(self) -> Db: ...
def refetch_schema(self) -> asyncio.Future: ...
def is_connected(self) -> bool: ...
def is_fully_connected(self) -> bool: ...
def get_version(self) -> tuple: ...


class MPInterval:
year: int
month: int
Expand All @@ -203,3 +206,16 @@ class MPInterval:
adjust: Adjust = Adjust.NONE,
): ...
def __eq__(self, other) -> bool: ...


class IProtoFeatures:
streams: bool
transactions: bool
error_extension: bool
watchers: bool
pagination: bool
space_and_index_names: bool
watch_once: bool
dml_tuple_extension: bool
call_ret_tuple_extension: bool
call_arg_tuple_extension: bool
9 changes: 6 additions & 3 deletions asynctnt/iproto/protocol.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ cdef class BaseProtocol(CoreProtocol):
self._schema_fetch_in_progress = False
self._refetch_schema_future = None
self._db = self._create_db(<bint> False)
self._features = IProtoFeatures.__new__(IProtoFeatures)
self.execute = self._execute_bad

try:
Expand Down Expand Up @@ -257,9 +258,7 @@ cdef class BaseProtocol(CoreProtocol):
return
e = f.exception()
if not e:
logger.debug('Tarantool[%s:%s] identified successfully',
self.host, self.port)

self._features = (<Response> f.result()).result_
self.post_con_state = POST_CONNECTION_AUTH
self._post_con_state_machine()
else:
Expand Down Expand Up @@ -519,6 +518,10 @@ cdef class BaseProtocol(CoreProtocol):
def refetch_schema(self):
return self._refetch_schema()

@property
def features(self) -> IProtoFeatures:
return self._features


class Protocol(BaseProtocol, asyncio.Protocol):
pass
Expand Down
14 changes: 14 additions & 0 deletions asynctnt/iproto/response.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ cdef class Response:
bint _push_subscribe
BaseRequest request_
object _exception
object result_

readonly object _q
readonly object _push_event
Expand All @@ -51,3 +52,16 @@ cdef ssize_t response_parse_header(const char *buf, uint32_t buf_len,
cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
Response resp, BaseRequest req,
bint is_chunk) except -1

cdef class IProtoFeatures:
cdef:
readonly bint streams
readonly bint transactions
readonly bint error_extension
readonly bint watchers
readonly bint pagination
readonly bint space_and_index_names
readonly bint watch_once
readonly bint dml_tuple_extension
readonly bint call_ret_tuple_extension
readonly bint call_arg_tuple_extension
48 changes: 47 additions & 1 deletion asynctnt/iproto/response.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ from libc.stdint cimport uint32_t
from asynctnt.log import logger


@cython.final
cdef class IProtoFeatures:
def __repr__(self):
return (f"<IProtoFeatures"
f" streams={self.streams}"
f" transactions={self.transactions}"
f" error_extension={self.error_extension}"
f" watchers={self.watchers}"
f" pagination={self.pagination}"
f" space_and_index_names={self.space_and_index_names}"
f" watch_once={self.watch_once}"
f" dml_tuple_extension={self.dml_tuple_extension}"
f" call_ret_tuple_extension={self.call_ret_tuple_extension}"
f" call_arg_tuple_extension={self.call_arg_tuple_extension}"
f">"
)


@cython.final
@cython.freelist(REQUEST_FREELIST)
cdef class Response:
Expand All @@ -26,6 +44,7 @@ cdef class Response:
self.errmsg = None
self.error = None
self._rowcount = 0
self.result_ = None
self.body = None
self.encoding = None
self.metadata = None
Expand Down Expand Up @@ -451,6 +470,7 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
const char *s
list data
Field field
IProtoFeatures features

b = <const char *> buf
# mp_fprint(stdio.stdout, b)
Expand Down Expand Up @@ -540,7 +560,33 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
logger.debug("IProto version: %s", _decode_obj(&b, resp.encoding))

elif key == tarantool.IPROTO_FEATURES:
logger.debug("IProto features available: %s", _decode_obj(&b, resp.encoding))
features = <IProtoFeatures> IProtoFeatures.__new__(IProtoFeatures)

for item in _decode_obj(&b, resp.encoding):
if item == 0:
features.streams = 1
elif item == 1:
features.transactions = 1
elif item == 2:
features.error_extension = 1
elif item == 3:
features.watchers = 1
elif item == 4:
features.pagination = 1
elif item == 5:
features.space_and_index_names = 1
elif item == 6:
features.watch_once = 1
elif item == 7:
features.dml_tuple_extension = 1
elif item == 8:
features.call_ret_tuple_extension = 1
elif item == 9:
features.call_arg_tuple_extension = 1
else:
logger.debug("unknown iproto feature available: %d", item)

resp.result_ = features

elif key == tarantool.IPROTO_AUTH_TYPE:
logger.debug("IProto auth type: %s", _decode_obj(&b, resp.encoding))
Expand Down
49 changes: 49 additions & 0 deletions tests/test_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,3 +802,52 @@ async def state_checker():
await conn.call("box.info")
finally:
await conn.disconnect()

async def test__features(self):
async with asynctnt.Connection(host=self.tnt.host, port=self.tnt.port) as conn:
if not check_version(
self,
conn.version,
min=(2, 10),
max=(3, 0),
min_included=True,
max_included=False,
):
return

self.assertIsNotNone(conn.features)
self.assertTrue(conn.features.streams)
self.assertTrue(conn.features.watchers)
self.assertTrue(conn.features.error_extension)
self.assertTrue(conn.features.transactions)
self.assertTrue(conn.features.pagination)

self.assertFalse(conn.features.space_and_index_names)
self.assertFalse(conn.features.watch_once)
self.assertFalse(conn.features.dml_tuple_extension)
self.assertFalse(conn.features.call_ret_tuple_extension)
self.assertFalse(conn.features.call_arg_tuple_extension)

async def test__features_3_0(self):
async with asynctnt.Connection(host=self.tnt.host, port=self.tnt.port) as conn:
if not check_version(
self,
conn.version,
min=(3, 0),
min_included=True,
max_included=False,
):
return

self.assertIsNotNone(conn.features)
self.assertTrue(conn.features.streams)
self.assertTrue(conn.features.watchers)
self.assertTrue(conn.features.error_extension)
self.assertTrue(conn.features.transactions)
self.assertTrue(conn.features.pagination)

self.assertTrue(conn.features.space_and_index_names)
self.assertTrue(conn.features.watch_once)
self.assertTrue(conn.features.dml_tuple_extension)
self.assertTrue(conn.features.call_ret_tuple_extension)
self.assertTrue(conn.features.call_arg_tuple_extension)

0 comments on commit 495892f

Please sign in to comment.