diff --git a/.gitignore b/.gitignore index 9d3a8412..53a8daa2 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ test/data/*.key !test/data/localhost.enc.key test/data/*.pem test/data/*.srl + +.rocks diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb88ae8..27582a49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +- Support `fetch_schema` parameter for a connection (#219). ### Added diff --git a/docs/source/quick-start.rst b/docs/source/quick-start.rst index 27633012..81526276 100644 --- a/docs/source/quick-start.rst +++ b/docs/source/quick-start.rst @@ -359,7 +359,7 @@ Through the :class:`~tarantool.Connection` object, you can access >>> import tarantool >>> from tarantool.error import CrudModuleError, CrudModuleManyError, DatabaseError - >>> conn = tarantool.Connection(host='localhost',port=3301) + >>> conn = tarantool.Connection(host='localhost',port=3301,fetch_schema=False) >>> conn.crud_ conn.crud_count( conn.crud_insert( conn.crud_insert_object_many( diff --git a/tarantool/connection.py b/tarantool/connection.py index 7fd43b9e..73352662 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -579,7 +579,8 @@ def __init__(self, host, port, ssl_password_file=DEFAULT_SSL_PASSWORD_FILE, packer_factory=default_packer_factory, unpacker_factory=default_unpacker_factory, - auth_type=None): + auth_type=None, + fetch_schema=True): """ :param host: Server hostname or IP address. Use ``None`` for Unix sockets. @@ -736,6 +737,18 @@ def __init__(self, host, port, ``"chap-sha1"``. :type auth_type: :obj:`None` or :obj:`str`, optional + :param bool fetch_schema: If ``False``, schema is not loaded on connect + and schema updates are not automatically loaded. + As a result, these methods become unavailable: + :meth:`~tarantool.Connection.replace`, + :meth:`~tarantool.Connection.insert`, + :meth:`~tarantool.Connection.delete`, + :meth:`~tarantool.Connection.upsert`, + :meth:`~tarantool.Connection.update`, + :meth:`~tarantool.Connection.select`, + :meth:`~tarantool.Connection.space`. + :type fetch_schema: :obj:`bool`, optional + :raise: :exc:`~tarantool.error.ConfigurationError`, :meth:`~tarantool.Connection.connect` exceptions @@ -766,8 +779,9 @@ def __init__(self, host, port, self.socket_timeout = socket_timeout self.reconnect_delay = reconnect_delay self.reconnect_max_attempts = reconnect_max_attempts - self.schema = Schema(self) - self.schema_version = 1 + self.fetch_schema = fetch_schema + self.schema = None + self.schema_version = 0 self._socket = None self.connected = False self.error = True @@ -1023,7 +1037,11 @@ def connect(self): if self.transport == SSL_TRANSPORT: self.wrap_socket_ssl() self.handshake() - self.load_schema() + if self.fetch_schema: + self.schema = Schema(self) + self.load_schema() + else: + self.schema = None except SslError as e: raise e except Exception as e: @@ -1118,7 +1136,8 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): response = request.response_class(self, self._read_response()) break except SchemaReloadException as e: - self.update_schema(e.schema_version) + if self.schema is not None: + self.update_schema(e.schema_version) continue while response._code == IPROTO_CHUNK: @@ -1255,6 +1274,9 @@ def update_schema(self, schema_version): :meta private: """ + if self.schema is None: + self.schema = Schema(self) + self.schema_version = schema_version self.flush_schema() @@ -1269,6 +1291,19 @@ def flush_schema(self): self.schema.flush() self.load_schema() + def _schemaful_connection_check(self): + """ + Checks whether the connection is schemaful. + If the connection is schemaless, an exception will be thrown + about unsupporting the method in connection opened + with fetch_schema=False. + + :raise: :exc:`~tarantool.error.NotSupportedError` + """ + if self.schema is None: + raise NotSupportedError('This method is not available in ' + + 'connection opened with fetch_schema=False') + def call(self, func_name, *args, on_push=None, on_push_ctx=None): """ Execute a CALL request: call a stored Lua function. @@ -1366,11 +1401,14 @@ def replace(self, space_name, values, on_push=None, on_push_ctx=None): :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _replace: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/replace/ """ + self._schemaful_connection_check() + if isinstance(space_name, str): space_name = self.schema.get_space(space_name).sid if on_push is not None and not callable(on_push): @@ -1411,7 +1449,7 @@ def authenticate(self, user, password): password=self.password, auth_type=self._get_auth_type()) auth_response = self._send_request_wo_reconnect(request) - if auth_response.return_code == 0: + if auth_response.return_code == 0 and self.schema is not None: self.flush_schema() return auth_response @@ -1584,11 +1622,14 @@ def insert(self, space_name, values, on_push=None, on_push_ctx=None): :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _insert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/insert/ """ + self._schemaful_connection_check() + if isinstance(space_name, str): space_name = self.schema.get_space(space_name).sid if on_push is not None and not callable(on_push): @@ -1623,11 +1664,14 @@ def delete(self, space_name, key, *, index=0, on_push=None, on_push_ctx=None): :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _delete: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/delete/ """ + self._schemaful_connection_check() + key = wrap_key(key) if isinstance(space_name, str): space_name = self.schema.get_space(space_name).sid @@ -1682,11 +1726,14 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, on_push=None, on_ :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _upsert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/upsert/ """ + self._schemaful_connection_check() + if isinstance(space_name, str): space_name = self.schema.get_space(space_name).sid if isinstance(index, str): @@ -1770,11 +1817,14 @@ def update(self, space_name, key, op_list, *, index=0, on_push=None, on_push_ctx :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _update: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/update/ """ + self._schemaful_connection_check() + key = wrap_key(key) if isinstance(space_name, str): space_name = self.schema.get_space(space_name).sid @@ -1956,11 +2006,14 @@ def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, i :exc:`~tarantool.error.DatabaseError`, :exc:`~tarantool.error.SchemaError`, :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` + :exc:`~tarantool.error.SslError`, + :exc:`~tarantool.error.NotSupportedError` .. _select: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/select/ """ + self._schemaful_connection_check() + if iterator is None: iterator = ITERATOR_EQ if key is None or (isinstance(key, (list, tuple)) and @@ -1996,6 +2049,8 @@ def space(self, space_name): :raise: :exc:`~tarantool.error.SchemaError` """ + self._schemaful_connection_check() + return Space(self, space_name) def generate_sync(self): diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 362b7618..b5e5f4f4 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -378,7 +378,8 @@ def __init__(self, call_16=False, connection_timeout=CONNECTION_TIMEOUT, strategy_class=RoundRobinStrategy, - refresh_delay=POOL_REFRESH_DELAY): + refresh_delay=POOL_REFRESH_DELAY, + fetch_schema=True): """ :param addrs: List of dictionaries describing server addresses: @@ -452,6 +453,9 @@ def __init__(self, `box.info.ro`_ status background refreshes, in seconds. :type connection_timeout: :obj:`float`, optional + :param fetch_schema: Refer to + :paramref:`~tarantool.Connection.params.fetch_schema`. + :raise: :exc:`~tarantool.error.ConfigurationError`, :class:`~tarantool.Connection` exceptions @@ -500,7 +504,8 @@ def __init__(self, ssl_ciphers=addr['ssl_ciphers'], ssl_password=addr['ssl_password'], ssl_password_file=addr['ssl_password_file'], - auth_type=addr['auth_type']) + auth_type=addr['auth_type'], + fetch_schema=fetch_schema) ) if connect_now: diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index cc4def5a..2cb44ee3 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -283,7 +283,8 @@ def __init__(self, host=None, port=None, addrs=None, strategy_class=RoundRobinStrategy, cluster_discovery_function=None, - cluster_discovery_delay=CLUSTER_DISCOVERY_DELAY): + cluster_discovery_delay=CLUSTER_DISCOVERY_DELAY, + fetch_schema=True): """ :param host: Refer to :paramref:`~tarantool.Connection.params.host`. @@ -425,6 +426,9 @@ def __init__(self, host=None, port=None, list refresh. :type cluster_discovery_delay: :obj:`float`, optional + :param fetch_schema: Refer to + :paramref:`~tarantool.Connection.params.fetch_schema`. + :raises: :exc:`~tarantool.error.ConfigurationError`, :class:`~tarantool.Connection` exceptions, :class:`~tarantool.MeshConnection.connect` exceptions @@ -489,7 +493,8 @@ def __init__(self, host=None, port=None, ssl_ciphers=addr['ssl_ciphers'], ssl_password=addr['ssl_password'], ssl_password_file=addr['ssl_password_file'], - auth_type=addr['auth_type']) + auth_type=addr['auth_type'], + fetch_schema=fetch_schema) def connect(self): """ diff --git a/tarantool/request.py b/tarantool/request.py index f6f6dfcb..73ef9bd2 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -188,9 +188,13 @@ def header(self, length): """ self._sync = self.conn.generate_sync() - header = self._dumps({IPROTO_REQUEST_TYPE: self.request_type, - IPROTO_SYNC: self._sync, - IPROTO_SCHEMA_ID: self.conn.schema_version}) + header_fields = { + IPROTO_REQUEST_TYPE: self.request_type, + IPROTO_SYNC: self._sync, + } + if self.conn.schema is not None: + header_fields[IPROTO_SCHEMA_ID] = self.conn.schema_version + header = self._dumps(header_fields) return self._dumps(length + len(header)) + header diff --git a/test/suites/crud_server.lua b/test/suites/crud_server.lua index ba6fe82a..55d4fc48 100644 --- a/test/suites/crud_server.lua +++ b/test/suites/crud_server.lua @@ -1,7 +1,58 @@ #!/usr/bin/env tarantool -local crud = require('crud') -local vshard = require('vshard') +local function configure_crud_instance(primary_listen, crud, vshard) + box.schema.create_space( + 'tester', { + format = { + {name = 'id', type = 'unsigned'}, + {name = 'bucket_id', type = 'unsigned'}, + {name = 'name', type = 'string'}, + } + }) + box.space.tester:create_index('primary_index', { + parts = { + {field = 1, type = 'unsigned'}, + }, + }) + box.space.tester:create_index('bucket_id', { + parts = { + {field = 2, type = 'unsigned'}, + }, + unique = false, + }) + + -- Setup vshard. + _G.vshard = vshard + box.once('guest', function() + box.schema.user.grant('guest', 'super') + end) + local uri = 'guest@0.0.0.0:' .. primary_listen + local cfg = { + bucket_count = 300, + sharding = { + [box.info().cluster.uuid] = { + replicas = { + [box.info().uuid] = { + uri = uri, + name = 'storage', + master = true, + }, + }, + }, + }, + } + vshard.storage.cfg(cfg, box.info().uuid) + vshard.router.cfg(cfg) + vshard.router.bootstrap() + + -- Initialize crud. + crud.init_storage() + crud.init_router() + crud.cfg{stats = true} +end + +local crud_imported, crud = pcall(require, 'crud') +local vshard_imported, vshard = pcall(require, 'vshard') local admin_listen = os.getenv("ADMIN") local primary_listen = os.getenv("LISTEN") @@ -18,51 +69,17 @@ box.schema.user.grant( 'read,write,execute', 'universe' ) -box.schema.create_space( - 'tester', { - format = { - {name = 'id', type = 'unsigned'}, - {name = 'bucket_id', type = 'unsigned'}, - {name = 'name', type = 'string'}, - } -}) -box.space.tester:create_index('primary_index', { - parts = { - {field = 1, type = 'unsigned'}, - }, -}) -box.space.tester:create_index('bucket_id', { - parts = { - {field = 2, type = 'unsigned'}, - }, - unique = false, -}) - --- Setup vshard. -_G.vshard = vshard -box.once('guest', function() - box.schema.user.grant('guest', 'super') -end) -local uri = 'guest@0.0.0.0:' .. primary_listen -local cfg = { - bucket_count = 300, - sharding = { - [box.info().cluster.uuid] = { - replicas = { - [box.info().uuid] = { - uri = uri, - name = 'storage', - master = true, - }, - }, - }, - }, -} -vshard.storage.cfg(cfg, box.info().uuid) -vshard.router.cfg(cfg) -vshard.router.bootstrap() --- Initialize crud. -crud.init_storage() -crud.init_router() -crud.cfg{stats = true} +if crud_imported == false or vshard_imported == false then + -- Set flag for unittest. + _G['ROCKS_IMPORT_FAIL'] = true + local fail_msg = 'The crud/vshard modules are not detected, ' .. + 'installation via rocks install is required ' .. + 'for CRUD testing purposes. You can use ' .. + ' or ' .. + ' to install modules' + -- The print output will be captured in the logs. + print(fail_msg) +else + configure_crud_instance(primary_listen, crud, vshard) +end diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 633b7159..4af62d90 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -33,13 +33,20 @@ def setUp(self): time.sleep(1) # Open connections to instance. self.conn = tarantool.Connection(host=self.host, port=self.port, - user='guest', password='') + user='guest', password='', fetch_schema=False) self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port, - user='guest', password='') + user='guest', password='', fetch_schema=False) self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], - user='guest', password='') + user='guest', password='', + fetch_schema=False) # Time for vshard group configuration. time.sleep(1) + if self.conn.eval('return ROCKS_IMPORT_FAIL').data[0] == True: + raise unittest.SkipTest('The crud/vshard modules are not detected, ' + + 'installation via rocks install is required ' + + 'for CRUD testing purposes. You can use ' + + ' or ' + + ' to install modules') crud_test_cases = { 'crud_insert': { diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 09017b2a..7e26b1a0 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -2,6 +2,7 @@ import unittest import tarantool from .lib.tarantool_server import TarantoolServer +from tarantool.error import NotSupportedError # FIXME: I'm quite sure that there is a simpler way to count @@ -41,8 +42,47 @@ def setUpClass(self): self.srv = TarantoolServer() self.srv.script = 'test/suites/box.lua' self.srv.start() + self.srv.admin("box.schema.user.create('test', {password = 'test', " + + "if_not_exists = true})") + self.srv.admin("box.schema.user.grant('test', 'read,write,execute', 'universe')") + + # Create server_function and tester space (for fetch_schema opt testing purposes). + self.srv.admin("function server_function() return 2+2 end") + self.srv.admin(""" + box.schema.create_space( + 'tester', { + format = { + {name = 'id', type = 'unsigned'}, + {name = 'name', type = 'string', is_nullable = true}, + } + }) + """) + self.srv.admin(""" + box.space.tester:create_index( + 'primary_index', { + parts = { + {field = 1, type = 'unsigned'}, + } + }) + """) + self.srv.admin("box.space.tester:insert({1, null})") + self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - encoding=self.encoding) + encoding=self.encoding, user='test', password='test') + self.con_schema_disable = tarantool.Connection(self.srv.host, self.srv.args['primary'], + encoding=self.encoding, fetch_schema=False, + user='test', password='test') + if not sys.platform.startswith("win"): + # Schema fetch disable tests via mesh and pool connection + # are not supported on windows platform. + self.mesh_con_schema_disable = tarantool.MeshConnection(host=self.srv.host, + port=self.srv.args['primary'], + fetch_schema=False, + user='test', password='test') + self.pool_con_schema_disable = tarantool.ConnectionPool([{'host':self.srv.host, + 'port':self.srv.args['primary']}], + user='test', password='test', + fetch_schema=False) self.sch = self.con.schema # The relevant test cases mainly target Python 2, where @@ -97,12 +137,6 @@ def verify_unicode_index(self, index): self.assertEqual(index.name, self.unicode_index_name_u) self.assertEqual(len(index.parts), 1) - def test_00_authenticate(self): - self.assertIsNone(self.srv.admin("box.schema.user.create('test', { password = 'test' })")) - self.assertIsNone(self.srv.admin("box.schema.user.grant('test', 'read,write', 'space', '_space')")) - self.assertIsNone(self.srv.admin("box.schema.user.grant('test', 'read,write', 'space', '_index')")) - self.assertEqual(self.con.authenticate('test', 'test')._data, None) - def test_01_space_bad(self): with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): @@ -341,9 +375,181 @@ def test_07_schema_version_update(self): self.srv.admin("box.schema.create_space('ttt22')") self.assertEqual(len(self.con.select('_space')), _space_len + 1) + # For schema fetch disable testing purposes. + testing_methods = { + 'unavailable': { + 'replace': { + 'input': ['tester', (1, None)], + 'output': [[1, None]], + }, + 'delete': { + 'input': ['tester', 1], + 'output': [[1, None]], + }, + 'insert': { + 'input': ['tester', (1, None)], + 'output': [[1, None]], + }, + 'upsert': { + 'input': ['tester', (1, None), []], + 'output': [], + }, + 'update': { + 'input': ['tester', 1, []], + 'output': [[1, None]], + }, + 'select': { + 'input': ['tester', 1], + 'output': [[1, None]], + }, + 'space': { + 'input': ['tester'], + }, + }, + 'available': { + # CRUD methods are also tested with the fetch_schema=False opt, + # see the test_crud.py file. + 'call': { + 'input': ['server_function'], + 'output': [4], + }, + 'eval': { + 'input': ['return 2+2'], + 'output': [4], + }, + 'ping': { + 'input': [], + }, + }, + } + + def _run_test_schema_fetch_disable(self, con, mode=None): + # Enable SQL test case for tarantool 2.* and higher. + if int(self.srv.admin.tnt_version.__str__()[0]) > 1: + self.testing_methods['available']['execute'] = { + 'input': ['SELECT * FROM "tester"'], + 'output': [[1, None]], + } + + # Testing the schemaless connection with methods + # that should NOT be available. + if mode is not None: + for addr in con.pool.keys(): + self.assertEqual(con.pool[addr].conn.schema_version, 0) + self.assertEqual(con.pool[addr].conn.schema, None) + else: + self.assertEqual(con.schema_version, 0) + self.assertEqual(con.schema, None) + for method_case in self.testing_methods['unavailable'].keys(): + with self.subTest(name=method_case): + if isinstance(con, tarantool.ConnectionPool) and method_case == 'space': + continue + testing_function = getattr(con, method_case) + try: + if mode is not None: + _ = testing_function( + *self.testing_methods['unavailable'][method_case]['input'], + mode=mode) + else: + _ = testing_function( + *self.testing_methods['unavailable'][method_case]['input']) + except NotSupportedError as e: + self.assertEqual(e.message, 'This method is not available in ' + + 'connection opened with fetch_schema=False') + # Testing the schemaless connection with methods + # that should be available. + for method_case in self.testing_methods['available'].keys(): + with self.subTest(name=method_case): + testing_function = getattr(con, method_case) + if mode is not None: + resp = testing_function( + *self.testing_methods['available'][method_case]['input'], + mode=mode) + else: + resp = testing_function( + *self.testing_methods['available'][method_case]['input']) + if method_case == 'ping': + self.assertEqual(isinstance(resp, float), True) + else: + self.assertEqual( + resp.data, + self.testing_methods['available'][method_case]['output']) + + # Turning the same connection into schemaful. + if mode is not None: + for addr in con.pool.keys(): + con.pool[addr].conn.update_schema(con.pool[addr].conn.schema_version) + else: + con.update_schema(con.schema_version) + + # Testing the schemaful connection with methods + # that should NOW be available. + for method_case in self.testing_methods['unavailable'].keys(): + with self.subTest(name=method_case): + if isinstance(con, tarantool.ConnectionPool) and method_case == 'space': + continue + testing_function = getattr(con, method_case) + if mode is not None: + resp = testing_function( + *self.testing_methods['unavailable'][method_case]['input'], + mode=mode) + else: + resp = testing_function( + *self.testing_methods['unavailable'][method_case]['input']) + if method_case == 'space': + self.assertEqual(isinstance(resp, tarantool.space.Space), True) + else: + self.assertEqual( + resp.data, + self.testing_methods['unavailable'][method_case]['output']) + # Testing the schemaful connection with methods + # that should have remained available. + for method_case in self.testing_methods['available'].keys(): + with self.subTest(name=method_case): + testing_function = getattr(con, method_case) + if mode is not None: + resp = testing_function( + *self.testing_methods['available'][method_case]['input'], + mode=mode) + else: + resp = testing_function( + *self.testing_methods['available'][method_case]['input']) + if method_case == 'ping': + self.assertEqual(isinstance(resp, float), True) + else: + self.assertEqual( + resp.data, + self.testing_methods['available'][method_case]['output']) + if mode is not None: + self.assertNotEqual(con.pool[addr].conn.schema_version, 1) + self.assertNotEqual(con.pool[addr].conn.schema, None) + else: + self.assertNotEqual(con.schema_version, 1) + self.assertNotEqual(con.schema, None) + + def test_08_schema_fetch_disable_via_connection(self): + self._run_test_schema_fetch_disable(self.con_schema_disable) + + @unittest.skipIf(sys.platform.startswith("win"), + 'Schema fetch disable tests via mesh connection on windows platform are not supported') + def test_08_schema_fetch_disable_via_mesh_connection(self): + self._run_test_schema_fetch_disable(self.mesh_con_schema_disable) + + @unittest.skipIf(sys.platform.startswith("win"), + 'Schema fetch disable tests via connection pool on windows platform are not supported') + def test_08_schema_fetch_disable_via_connection_pool(self): + self._run_test_schema_fetch_disable(self.pool_con_schema_disable, + mode=tarantool.Mode.ANY) + @classmethod def tearDownClass(self): self.con.close() + self.con_schema_disable.close() + if not sys.platform.startswith("win"): + # Schema fetch disable tests via mesh and pool connection + # are not supported on windows platform. + self.mesh_con_schema_disable.close() + self.pool_con_schema_disable.close() self.srv.stop() self.srv.clean()