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 Dec 31, 2023
1 parent 2277115 commit c1de1fd
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 8 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ jobs:
matrix:
os: [ ubuntu-latest, macos-latest ]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.10']
tarantool: ['1.10', '2']
tarantool: ['1.10', '2', '3']
exclude:
- os: macos-latest
tarantool: '1.10'
- os: macos-latest
tarantool: '3'
- python-version: 'pypy3.10'
tarantool: '1.10'
- python-version: 'pypy3.10'
tarantool: '3'

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

Expand Down
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
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 @@ -69,6 +69,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
14 changes: 14 additions & 0 deletions asynctnt/iproto/protocol.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,23 @@ 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 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 @@ -98,6 +98,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 @@ -253,9 +254,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 @@ -515,6 +514,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 @@ -41,6 +41,7 @@ cdef class Response:
bint _push_subscribe
BaseRequest request_
object _exception
object result_

readonly object _q
readonly object _push_event
Expand All @@ -66,3 +67,16 @@ cdef ssize_t response_parse_body(const char *buf, uint32_t buf_len,
Response resp, BaseRequest req,
bint is_chunk) except -1
cdef IProtoError parse_iproto_error(const char ** b, bytes encoding)

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
47 changes: 46 additions & 1 deletion asynctnt/iproto/response.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ cdef class IProtoErrorStackFrame:
cdef class IProtoError:
pass

@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 @@ -41,6 +58,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 @@ -546,6 +564,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 @@ -635,7 +654,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
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def initialize_options(self):
self.debug = True
self.gdb_debug = True
else:
self.cython_always = False
self.cython_always = True
self.cython_annotate = None
self.cython_directives = None
self.gdb_debug = False
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 c1de1fd

Please sign in to comment.