From 24002971dbdf8c04f104156655b4d6e83b1ed5f5 Mon Sep 17 00:00:00 2001 From: Ilya Grishnov Date: Thu, 12 Jan 2023 12:16:48 +0300 Subject: [PATCH 001/190] test: fixes the moduleless error of crud instance In crud tests, if the necessary rocks dependencies are missing, an error message is now displayed. Prior to this fix, an error in importing missing models caused the tests to freez forever. Part of #205 --- test/suites/crud_server.lua | 115 +++++++++++++++++++++--------------- test/suites/test_crud.py | 6 ++ 2 files changed, 72 insertions(+), 49 deletions(-) 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..02f8c6b8 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -40,6 +40,12 @@ def setUp(self): user='guest', password='') # 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': { From ca0a514ec3ef4ebff928ad81145e2bb6fc34edee Mon Sep 17 00:00:00 2001 From: Ilya Grishnov Date: Thu, 12 Jan 2023 12:21:07 +0300 Subject: [PATCH 002/190] api: extend connect with fetch_schema param Added support of the fetch_schema parameter, which allows to ignore schema changes on the server. Closes #219 --- .gitignore | 2 + CHANGELOG.md | 1 + docs/source/quick-start.rst | 2 +- tarantool/connection.py | 79 +++++++++++-- tarantool/connection_pool.py | 9 +- tarantool/mesh_connection.py | 9 +- tarantool/request.py | 10 +- test/suites/test_crud.py | 7 +- test/suites/test_schema.py | 220 +++++++++++++++++++++++++++++++++-- 9 files changed, 309 insertions(+), 30 deletions(-) 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/test_crud.py b/test/suites/test_crud.py index 02f8c6b8..4af62d90 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -33,11 +33,12 @@ 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: 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() From c8bdb90b0165b6abc7fbf37afe6f069cb332cda5 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 10 Feb 2023 11:15:53 +0300 Subject: [PATCH 003/190] ci: fix pip branch install on forks Before this patch, pip branch install pipelines had failed if CI is triggered with a PR from a fork [1]. 1. https://github.com/tarantool/tarantool-python/actions/runs/4052831981/jobs/7162061474 --- .github/workflows/testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 6c18acd0..a47f6efa 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -212,7 +212,7 @@ jobs: run: python3 .github/scripts/remove_source_code.py - name: Install the package with pip - run: pip3 install git+$GITHUB_SERVER_URL/$GITHUB_REPOSITORY@$GITHUB_REF_NAME + run: pip3 install git+$GITHUB_SERVER_URL/$GITHUB_REPOSITORY@$GITHUB_REF - name: Install test requirements run: pip3 install -r requirements-test.txt @@ -340,7 +340,7 @@ jobs: run: python3 .github/scripts/remove_source_code.py - name: Install the package with pip - run: pip3 install git+$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY@$env:GITHUB_REF_NAME + run: pip3 install git+$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY@$env:GITHUB_REF - name: Install test requirements run: pip3 install -r requirements-test.txt From 03c21a377aa4de7c5766e51a5428aa3e9f1cf958 Mon Sep 17 00:00:00 2001 From: Bekhzod Date: Tue, 31 Jan 2023 09:51:12 +0000 Subject: [PATCH 004/190] fix: misspell on return code --- CHANGELOG.md | 1 + tarantool/connection.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27582a49..ed1028fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed ### Fixed +- Error code on socket error (#279). ## 0.11.0 - 2022-12-31 diff --git a/tarantool/connection.py b/tarantool/connection.py index 73352662..b2d17760 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1168,7 +1168,7 @@ def check(): # Check that connection is alive sock_fd = self._socket.fileno() except socket.error as e: if e.errno == errno.EBADF: - return errno.ECONNRESETtuple_value + return errno.ECONNRESET else: if os.name == 'nt': flag = socket.MSG_PEEK From 1ab902dd44a32df6a13af027f2e0332375339f6f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 13 Feb 2023 11:41:39 +0300 Subject: [PATCH 005/190] changelog: fix unreleased entry Follows #271 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1028fc..d2291c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ 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 +- Support `fetch_schema` parameter for a connection (#219). ### Changed From 9681228fdafb34b65450ca653ef1de2272a86520 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 13 Feb 2023 11:45:17 +0300 Subject: [PATCH 006/190] deb: fix release author entries in changelog --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 0c13aaea..8aafe69e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,7 +18,7 @@ python3-tarantool (0.11.0-0) unstable; urgency=medium - Support specifying authentication method with `auth_type` and Tarantool EE `pap-sha256` authentication method (#269). - -- Georgy.moiseev Sat, 31 Dec 2022 02:09:03 +0300 + -- Georgy Moiseev Sat, 31 Dec 2022 02:09:03 +0300 python3-tarantool (0.10.0-0) unstable; urgency=medium @@ -223,7 +223,7 @@ python3-tarantool (0.10.0-0) unstable; urgency=medium - Pack and publish pip, RPM and deb packages with GitHub Actions (#164, #198). - Publish on readthedocs with CI/CD (including PRs) (#67). - -- Georgy.moiseev Wed, 09 Nov 2022 13:14:20 +0300 + -- Georgy Moiseev Wed, 09 Nov 2022 13:14:20 +0300 tarantool-python (0.9.0-0) unstable; urgency=medium ## Overview From 4f6c5d168453edf08ff781ade355bc1f49e25a2c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 13 Feb 2023 11:51:53 +0300 Subject: [PATCH 007/190] release: 0.12.0 Overview This release introduces the support of `fetch_schema` connection option to disable schema fetch and various fixes. Breaking changes This release should not break any existing behavior. New features - `fetch_schema` parameter for a connection (#219). Bugfixes - Error code on socket error (#279). Thanks We want to thank @bekhzod91 for a bugfix contribution. --- CHANGELOG.md | 4 +--- debian/changelog | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2291c08..62a69014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,11 @@ All notable changes to this project will be documented in this file. 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 +## 0.12.0 - 2023-02-13 ### Added - Support `fetch_schema` parameter for a connection (#219). -### Changed - ### Fixed - Error code on socket error (#279). diff --git a/debian/changelog b/debian/changelog index 8aafe69e..e862fd59 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,25 @@ +python3-tarantool (0.12.0-0) unstable; urgency=medium + + ## Overview + + This release introduces the support of `fetch_schema` connection + option to disable schema fetch and various fixes. + + ### Breaking changes + + This release should not break any existing behavior. + + ### New features + - `fetch_schema` parameter for a connection (#219). + + ### Bugfixes + - Error code on socket error (#279). + + ### Thanks + We want to thank @bekhzod91 for a bugfix contribution. + + -- Georgy Moiseev Mon, 13 Feb 2023 11:43:30 +0300 + python3-tarantool (0.11.0-0) unstable; urgency=medium ## Overview From 6fb3490bdb6b89e51cfcf5283fb2759dd2280a62 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 13 Feb 2023 13:59:49 +0300 Subject: [PATCH 008/190] deb: fix changelog entry format Follows #281 --- debian/changelog | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index e862fd59..cf29e63f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,17 +5,17 @@ python3-tarantool (0.12.0-0) unstable; urgency=medium This release introduces the support of `fetch_schema` connection option to disable schema fetch and various fixes. - ### Breaking changes + ## Breaking changes This release should not break any existing behavior. - ### New features + ## New features - `fetch_schema` parameter for a connection (#219). - ### Bugfixes + ## Bugfixes - Error code on socket error (#279). - ### Thanks + ## Thanks We want to thank @bekhzod91 for a bugfix contribution. -- Georgy Moiseev Mon, 13 Feb 2023 11:43:30 +0300 From 599e3f4e643019772000563a1f8133c2ed91bcdf Mon Sep 17 00:00:00 2001 From: Oleg Babin Date: Mon, 27 Feb 2023 06:56:50 +0300 Subject: [PATCH 009/190] iproto: disable feature discovery for specific versions "IProto features" are available since Tarantool 2.10 and there is no need to even try to fetch them for lower versions. Such check there is in netbox code [1]. Before this patch cartridge remote control closed the connection after RequestProtocolVersion however but for netbox it worked fine. So let's uniform behaviour between connectors adding a small check to skip request if there is a gurarntee that "IProto features" are not supported. Closes https://github.com/tarantool/tarantool-python/issues/283 [1] https://github.com/tarantool/tarantool/pull/6434/commits/2cbec82d8451670967252862893795dcf6371adb#diff-a47383908beda20a06c9f64bf5c3d4c51879034e38bcdccdf45210e89ce6dfb7R1947 --- CHANGELOG.md | 5 +++++ tarantool/connection.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62a69014..59fcc06e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Changed +- Discovery iproto features only for Tarantools since version 2.10.0 (#283). + ## 0.12.0 - 2023-02-13 ### Added diff --git a/tarantool/connection.py b/tarantool/connection.py index b2d17760..cd1329c6 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1014,7 +1014,8 @@ def handshake(self): if greeting.protocol != "Binary": raise NetworkError("Unsupported protocol: " + greeting.protocol) self.version_id = greeting.version_id - self._check_features() + if self.version_id >= version_id(2, 10, 0): + self._check_features() self.uuid = greeting.uuid self._salt = greeting.salt if self.user: From 0622297d002d03702e7b446a88dab73ab7c67164 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Feb 2023 14:19:54 +0300 Subject: [PATCH 010/190] internal: remove unreachable code in schema Part of #282 --- tarantool/schema.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index d4f13f6b..b60538e6 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -97,7 +97,6 @@ def __init__(self, index_row, space): """ self.iid = index_row[1] - self.name = index_row[2] self.name = to_unicode(index_row[2]) self.index = index_row[3] self.unique = index_row[4] From 56dfa4de83025f5515029fc9dcb0f5b4a097c029 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Feb 2023 14:29:44 +0300 Subject: [PATCH 011/190] iproto: fix schema with constraints fetch Before this patch, only schemas with 3-level nesting were expected. Simple foreign keys schema has 4-level nesting. After this patch, nesting depth up to 32 is supported. (There are no known schemas with such nesting, but this should be enough for any future extensions.) Closes #282 --- CHANGELOG.md | 3 +++ tarantool/schema.py | 11 ++++++-- test/suites/lib/skip.py | 11 ++++++++ test/suites/test_schema.py | 54 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59fcc06e..357b95f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Discovery iproto features only for Tarantools since version 2.10.0 (#283). +### Fixed +- Schema fetch for spaces with foreign keys (#282). + ## 0.12.0 - 2023-02-13 ### Added diff --git a/tarantool/schema.py b/tarantool/schema.py index b60538e6..94f49048 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -12,6 +12,13 @@ import tarantool.const as const +""" +Max possible known schema depth is 4 if foreign keys are used (since +Tarantool 2.10), but there are no restrictions in protocol. +""" +MAX_RECURSION_DEPTH = 32 + + class RecursionError(Error): """ Report the situation when max recursion depth is reached. @@ -102,7 +109,7 @@ def __init__(self, index_row, space): self.unique = index_row[4] self.parts = [] try: - parts_raw = to_unicode_recursive(index_row[5], 3) + parts_raw = to_unicode_recursive(index_row[5], MAX_RECURSION_DEPTH) except RecursionError as e: errmsg = 'Unexpected index parts structure: ' + str(e) raise SchemaError(errmsg) @@ -159,7 +166,7 @@ def __init__(self, space_row, schema): self.schema[self.name] = self self.format = dict() try: - format_raw = to_unicode_recursive(space_row[6], 3) + format_raw = to_unicode_recursive(space_row[6], MAX_RECURSION_DEPTH) except RecursionError as e: errmsg = 'Unexpected space format structure: ' + str(e) raise SchemaError(errmsg) diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 5cc40d65..c37fba0d 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -236,3 +236,14 @@ def skip_or_run_auth_type_test_call(self): return skip_or_run_test_tarantool_call(self, '2.11.0', 'does not support auth type') + +def skip_or_run_constraints_test(func): + """Decorator to skip or run tests related to spaces with + schema constraints. + + Tarantool supports schema constraints only since 2.10.0 version. + See https://github.com/tarantool/tarantool/issues/6436 + """ + + return skip_or_run_test_tarantool(func, '2.10.0', + 'does not support schema constraints') diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 7e26b1a0..55460018 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,10 @@ import sys import unittest import tarantool +import pkg_resources + from .lib.tarantool_server import TarantoolServer +from .lib.skip import skip_or_run_constraints_test from tarantool.error import NotSupportedError @@ -102,6 +105,33 @@ def setUpClass(self): end """) + if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): + self.srv.admin(""" + box.schema.create_space( + 'constr_tester_1', { + format = { + { name = 'id', type = 'unsigned' }, + { name = 'payload', type = 'number' }, + } + }) + box.space.constr_tester_1:create_index('I1', { parts = {'id'} }) + + box.space.constr_tester_1:replace({1, 999}) + + box.schema.create_space( + 'constr_tester_2', { + format = { + { name = 'id', type = 'unsigned' }, + { name = 'table1_id', type = 'unsigned', + foreign_key = { fk_video = { space = 'constr_tester_1', field = 'id' } }, + }, + { name = 'payload', type = 'number' }, + } + }) + box.space.constr_tester_2:create_index('I1', { parts = {'id'} }) + box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} }) + """) + def setUp(self): # prevent a remote tarantool from clean our session if self.srv.is_started(): @@ -541,8 +571,32 @@ 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) + @skip_or_run_constraints_test + def test_09_foreign_key_info_fetched_to_schema(self): + self.assertIn('foreign_key', self.sch.get_space('constr_tester_2').format['table1_id']) + + @skip_or_run_constraints_test + def test_10_foreign_key_valid_replace(self): + self.assertSequenceEqual( + self.con.replace('constr_tester_2', [1, 1, 623]), + [[1, 1, 623]]) + + @skip_or_run_constraints_test + def test_11_foreign_key_invalid_replace(self): + with self.assertRaisesRegex(tarantool.DatabaseError, + 'foreign tuple was not found'): + self.con.replace('constr_tester_2', [2, 999, 623]) + @classmethod def tearDownClass(self): + # We need to drop spaces with foreign keys with predetermined order, + # otherwise remote server clean() will fail to clean up resources. + if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): + self.srv.admin(""" + box.space.constr_tester_2:drop() + box.space.constr_tester_1:drop() + """) + self.con.close() self.con_schema_disable.close() if not sys.platform.startswith("win"): From 2adcde62830bdccacbeef80e4783c88ba9605435 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Feb 2023 18:41:11 +0300 Subject: [PATCH 012/190] ci: bump actions to use Node.js 16 Action based on Node.js 12 are deprecated in GitHub Actions now [1]. 1. https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/ --- .github/workflows/packing.yml | 2 +- .github/workflows/reusable_testing.yml | 6 +++--- .github/workflows/testing.yml | 30 +++++++++++++------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index 00ef82ab..17118fe6 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -79,7 +79,7 @@ jobs: run: python3 .github/scripts/remove_source_code.py - name: Install tarantool - uses: tarantool/setup-tarantool@v1 + uses: tarantool/setup-tarantool@v2 with: tarantool-version: '2.10' diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index 1e66af13..f1b594eb 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Clone the tarantool-python connector - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ github.repository_owner }}/tarantool-python - name: Download the tarantool build artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: ${{ inputs.artifact_name }} @@ -29,7 +29,7 @@ jobs: run: sudo dpkg -i tarantool*.deb - name: Setup python3 for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a47f6efa..90ae0e49 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -56,15 +56,15 @@ jobs: steps: - name: Clone the connector - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install tarantool ${{ matrix.tarantool }} - uses: tarantool/setup-tarantool@v1 + uses: tarantool/setup-tarantool@v2 with: tarantool-version: ${{ matrix.tarantool }} - name: Setup Python for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -127,14 +127,14 @@ jobs: - name: Clone the connector # `ref` as merge request is needed for pull_request_target because this # target runs in the context of the base commit of the pull request. - uses: actions/checkout@v2 + uses: actions/checkout@v3 if: github.event_name == 'pull_request_target' with: ref: refs/pull/${{ github.event.pull_request.number }}/merge - name: Clone the connector if: github.event_name != 'pull_request_target' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Tarantool EE SDK run: | @@ -144,7 +144,7 @@ jobs: rm -f ${ARCHIVE_NAME} - name: Setup Python for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -196,15 +196,15 @@ jobs: - '3.10' steps: - name: Clone the connector repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install tarantool ${{ matrix.tarantool }} - uses: tarantool/setup-tarantool@v1 + uses: tarantool/setup-tarantool@v2 with: tarantool-version: ${{ matrix.tarantool }} - name: Setup Python for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -252,10 +252,10 @@ jobs: steps: - name: Clone the connector - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Python for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -266,7 +266,7 @@ jobs: run: pip3 install -r requirements-test.txt - name: Setup WSL for tarantool - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 with: distribution: Ubuntu-20.04 @@ -329,10 +329,10 @@ jobs: - '3.10' steps: - name: Clone the connector repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Python for tests - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -346,7 +346,7 @@ jobs: run: pip3 install -r requirements-test.txt - name: Setup WSL for tarantool - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 with: distribution: Ubuntu-20.04 From 223c3122c8b232aa42014bac1ead0fe0e7ff99bc Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Feb 2023 10:20:16 +0300 Subject: [PATCH 013/190] authors: update list --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index f77bd18d..92787ea1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,3 +35,4 @@ Sergey Bronnikov Yaroslav Lobankov Georgy Moiseev Oleg Jukovec +Ilya Grishnov From 5abae5d898cd3e3a32ea26d124e53f23bdde85c4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Feb 2023 10:25:13 +0300 Subject: [PATCH 014/190] release: 0.12.1 Overview This release introduces several bug fixes and behavior improvements. Breaking changes This release should not break any existing behavior. Bugfixes - Discovery iproto features only for Tarantools since version 2.10.0 (#283). - Schema fetch for spaces with foreign keys (#282). --- CHANGELOG.md | 2 +- debian/changelog | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 357b95f6..957c2312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. 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 +## 0.12.1 - 2023-02-28 ### Changed - Discovery iproto features only for Tarantools since version 2.10.0 (#283). diff --git a/debian/changelog b/debian/changelog index cf29e63f..d6d83b6a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,19 @@ +python3-tarantool (0.12.1-0) unstable; urgency=medium + + ## Overview + + This release introduces several bugfixes and behavior improvements. + + ## Breaking changes + + This release should not break any existing behavior. + + ## Bugfixes + - Discovery iproto features only for Tarantools since version 2.10.0 (#283). + - Schema fetch for spaces with foreign keys (#282). + + -- Georgy Moiseev Tue, 28 Feb 2023 10:20:48 +0300 + python3-tarantool (0.12.0-0) unstable; urgency=medium ## Overview From d70d1acad95bcdede5b4a4b742681c41e5287aa7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Mar 2023 15:59:23 +0300 Subject: [PATCH 015/190] doc: bump sphinx favicon extension Since 1.0, "sphinx-favicon" has been renamed to "sphinx_favicon". Closes #287 --- docs/requirements.txt | 2 +- docs/source/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 964c9f18..a5b9de39 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ sphinx==5.2.1 sphinx-paramlinks==0.5.4 -sphinx-favicon==0.2 +sphinx_favicon==1.0.1 diff --git a/docs/source/conf.py b/docs/source/conf.py index 4738e61d..cea15f49 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,7 +25,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx_paramlinks', - 'sphinx-favicon',] + 'sphinx_favicon',] autodoc_default_options = { 'members': True, @@ -126,7 +126,7 @@ # pixels large. #html_favicon = None -# Set up favicons with sphinx-favicon. +# Set up favicons with sphinx_favicon. favicons = [ { "rel": "icon", From 2c3d05d4aca08fc50404533e6fdeb072a0cca16e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 9 Sep 2022 15:17:20 +0300 Subject: [PATCH 016/190] pylint: remove old ignores Last PEP8 checkup was in 0.5.5 [1]. Many things has changed since in both PEP8 and connector source code. To ensure that everything is alright, this patch removes existing pylint ignores before pylint checkup. They could be restored after thorough checkup. 1. https://github.com/tarantool/tarantool-python/commit/93b5d3d49712ee409a1b446be8012c0290fd7009 Part of #270 --- tarantool/__init__.py | 2 -- tarantool/connection.py | 1 - tarantool/const.py | 2 -- tarantool/error.py | 3 +-- tarantool/request.py | 12 ------------ tarantool/response.py | 1 - tarantool/schema.py | 1 - tarantool/space.py | 1 - 8 files changed, 1 insertion(+), 22 deletions(-) diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 915961fd..071032c1 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -1,5 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 - import sys from tarantool.connection import Connection diff --git a/tarantool/connection.py b/tarantool/connection.py index cd1329c6..d38c4cc0 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1,4 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 """ This module provides API for interaction with a Tarantool server. """ diff --git a/tarantool/const.py b/tarantool/const.py index 6b12598d..edb34608 100644 --- a/tarantool/const.py +++ b/tarantool/const.py @@ -1,5 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 - IPROTO_REQUEST_TYPE = 0x00 IPROTO_SYNC = 0x01 # replication keys (header) diff --git a/tarantool/error.py b/tarantool/error.py index 29c2430f..ecd09c8a 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -1,4 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 """ Python DB API compatible exceptions, see `PEP-249`_. @@ -391,7 +390,7 @@ def warn(message, warning_class): :type warning_class: :class:`~tarantool.error.Warning` """ - frame = sys._getframe(2) # pylint: disable=W0212 + frame = sys._getframe(2) module_name = frame.f_globals.get("__name__") line_no = frame.f_lineno warnings.warn_explicit(message, warning_class, module_name, line_no) diff --git a/tarantool/request.py b/tarantool/request.py index 73ef9bd2..05771b0f 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -1,4 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 """ Request types definitions. For internal use only, there is no API to send pre-build request objects. @@ -206,7 +205,6 @@ class RequestInsert(Request): request_type = REQUEST_TYPE_INSERT - # pylint: disable=W0231 def __init__(self, conn, space_no, values): """ :param conn: Request sender. @@ -322,7 +320,6 @@ class RequestReplace(Request): request_type = REQUEST_TYPE_REPLACE - # pylint: disable=W0231 def __init__(self, conn, space_no, values): """ :param conn: Request sender. @@ -353,7 +350,6 @@ class RequestDelete(Request): request_type = REQUEST_TYPE_DELETE - # pylint: disable=W0231 def __init__(self, conn, space_no, index_no, key): """ :param conn: Request sender. @@ -387,7 +383,6 @@ class RequestSelect(Request): request_type = REQUEST_TYPE_SELECT - # pylint: disable=W0231 def __init__(self, conn, space_no, index_no, key, offset, limit, iterator): """ :param conn: Request sender. @@ -433,7 +428,6 @@ class RequestUpdate(Request): request_type = REQUEST_TYPE_UPDATE - # pylint: disable=W0231 def __init__(self, conn, space_no, index_no, key, op_list): """ :param conn: Request sender. @@ -473,7 +467,6 @@ class RequestCall(Request): request_type = REQUEST_TYPE_CALL - # pylint: disable=W0231 def __init__(self, conn, name, args, call_16): """ :param conn: Request sender. @@ -510,7 +503,6 @@ class RequestEval(Request): request_type = REQUEST_TYPE_EVAL - # pylint: disable=W0231 def __init__(self, conn, name, args): """ :param conn: Request sender. @@ -558,7 +550,6 @@ class RequestUpsert(Request): request_type = REQUEST_TYPE_UPSERT - # pylint: disable=W0231 def __init__(self, conn, space_no, index_no, tuple_value, op_list): """ :param conn: Request sender. @@ -598,7 +589,6 @@ class RequestJoin(Request): request_type = REQUEST_TYPE_JOIN - # pylint: disable=W0231 def __init__(self, conn, server_uuid): """ :param conn: Request sender. @@ -620,7 +610,6 @@ class RequestSubscribe(Request): request_type = REQUEST_TYPE_SUBSCRIBE - # pylint: disable=W0231 def __init__(self, conn, cluster_uuid, server_uuid, vclock): """ :param conn: Request sender. @@ -656,7 +645,6 @@ class RequestOK(Request): request_type = REQUEST_TYPE_OK - # pylint: disable=W0231 def __init__(self, conn, sync): """ :param conn: Request sender. diff --git a/tarantool/response.py b/tarantool/response.py index ece616d4..aae3539f 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -1,4 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 """ Request response types definitions. """ diff --git a/tarantool/schema.py b/tarantool/schema.py index 94f49048..ad96429f 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -1,4 +1,3 @@ -# pylint: disable=R0903 """ Schema types definitions. For internal use only, there is no API to use pre-build schema objects. diff --git a/tarantool/space.py b/tarantool/space.py index 0fa61198..e9198ae9 100644 --- a/tarantool/space.py +++ b/tarantool/space.py @@ -1,4 +1,3 @@ -# pylint: disable=C0301,W0105,W0401,W0614 """ Space type definition. It is an object-oriented wrapper for requests to a Tarantool server space. From 6edc24ef57fcbff7cdcd7b901b8253dbbcff3871 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 9 Sep 2022 15:25:19 +0300 Subject: [PATCH 017/190] test: add pylint dependency See [1]. 1. https://pypi.org/project/pylint/ Part of #270 --- requirements-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-test.txt b/requirements-test.txt index ae4091d2..f73f4d4b 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,3 +1,4 @@ git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-compliance pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' +pylint From 7a17802ee1b675d1c7480ceea9f7bda016fbb6cb Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Mar 2023 18:25:01 +0300 Subject: [PATCH 018/190] make: add lint command Part of #270 --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 1ab6441b..f317cb8c 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,12 @@ install: pip3 install --editable . +PYTHON_FILES=tarantool test setup.py docs/source/conf.py +.PHONY: lint +lint: + python3 -m pylint --recursive=y $(PYTHON_FILES) + + .PHONY: test test: python3 setup.py test From 65a76559c6226d3cf7bda616badeb572ae160d75 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 27 Mar 2023 19:06:23 +0300 Subject: [PATCH 019/190] pylint: fix trailing-whitespace cases Fix all cases of C0303 trailing-whitespace. Remove trailing whitespaces from documentation as well. Part of #270 --- .github/workflows/testing.yml | 2 +- CHANGELOG.md | 4 +- README.rst | 2 +- debian/changelog | 4 +- debian/rules | 2 +- docs/source/conf.py | 2 +- docs/source/index.rst | 4 +- docs/source/quick-start.rst | 64 ++++++++--------- tarantool/connection.py | 64 ++++++++--------- tarantool/connection_pool.py | 94 ++++++++++++------------- tarantool/crud.py | 6 +- tarantool/error.py | 4 +- tarantool/msgpack_ext/decimal.py | 4 +- tarantool/msgpack_ext/types/interval.py | 2 +- test/suites/test_crud.py | 80 ++++++++++----------- test/suites/test_datetime.py | 2 +- test/suites/test_dbapi.py | 12 ++-- test/suites/test_decimal.py | 2 +- test/suites/test_error_ext.py | 2 +- test/suites/test_interval.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_push.py | 6 +- test/suites/test_schema.py | 30 ++++---- 23 files changed, 198 insertions(+), 198 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 90ae0e49..c49f8b92 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -37,7 +37,7 @@ jobs: - '' # Adding too many elements to three-dimentional matrix results in - # too many test cases. It causes GitHub webpages to fail with + # too many test cases. It causes GitHub webpages to fail with # "This page is taking too long to load." error. Thus we use # pairwise testing. include: diff --git a/CHANGELOG.md b/CHANGELOG.md index 957c2312..d2c49812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.12.1 - 2023-02-28 ### Changed -- Discovery iproto features only for Tarantools since version 2.10.0 (#283). +- Discovery iproto features only for Tarantools since version 2.10.0 (#283). ### Fixed - Schema fetch for spaces with foreign keys (#282). @@ -352,7 +352,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Various improvements and fixes in README (PR #210, PR #215). ### Fixed -- json.dumps compatibility with Python 2 (PR #186). +- json.dumps compatibility with Python 2 (PR #186). - Unix socket support in mesh_connection (PR #189, #111). - Various fixes in tests (PR #189, #111, PR #195, #194). diff --git a/README.rst b/README.rst index 41aa6a62..7e95ec3a 100644 --- a/README.rst +++ b/README.rst @@ -86,7 +86,7 @@ You can also install the development version of the package using ``pip``. What is Tarantool? ------------------ -`Tarantool`_ is an in-memory computing platform originally designed by +`Tarantool`_ is an in-memory computing platform originally designed by `VK`_ and released under the terms of `BSD license`_. Features diff --git a/debian/changelog b/debian/changelog index d6d83b6a..c85ff501 100644 --- a/debian/changelog +++ b/debian/changelog @@ -290,7 +290,7 @@ tarantool-python (0.9.0-0) unstable; urgency=medium transport="ssl", ssl_ca_file=client_ca_file) ``` - + If the server authenticates clients using certificates issued by given CA, you must provide private SSL key file with `ssl_key_file` parameter and SSL certificate file with `ssl_cert_file` parameter. @@ -351,7 +351,7 @@ tarantool-python (0.9.0-0) unstable; urgency=medium ``` See [Tarantool Enterprise Edition manual](https://www.tarantool.io/en/enterprise_doc/security/#enterprise-iproto-encryption) - for details. + for details. ## Breaking changes diff --git a/debian/rules b/debian/rules index 658c3ceb..b55a2b66 100755 --- a/debian/rules +++ b/debian/rules @@ -14,7 +14,7 @@ override_dh_auto_build: python3 setup.py build --force override_dh_auto_install: - python3 setup.py install --force --root=debian/python3-tarantool --no-compile -O0 --install-layout=deb --prefix=/usr + python3 setup.py install --force --root=debian/python3-tarantool --no-compile -O0 --install-layout=deb --prefix=/usr override_dh_python2: dh_python2 --no-guessing-versions diff --git a/docs/source/conf.py b/docs/source/conf.py index cea15f49..83e49918 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -126,7 +126,7 @@ # pixels large. #html_favicon = None -# Set up favicons with sphinx_favicon. +# Set up favicons with sphinx_favicon. favicons = [ { "rel": "icon", diff --git a/docs/source/index.rst b/docs/source/index.rst index b2d31cd7..60a264fe 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,7 +5,7 @@ Python client library for Tarantool :Version: |version| -`Tarantool`_ is an in-memory computing platform originally designed by +`Tarantool`_ is an in-memory computing platform originally designed by `VK`_ and released under the terms of `BSD license`_. Install Tarantool Python connector with ``pip`` (`PyPI`_ page): @@ -78,7 +78,7 @@ API Reference .. Indices and tables .. ================== -.. +.. .. * :ref:`genindex` .. * :ref:`modindex` .. * :ref:`search` diff --git a/docs/source/quick-start.rst b/docs/source/quick-start.rst index 81526276..20dec4db 100644 --- a/docs/source/quick-start.rst +++ b/docs/source/quick-start.rst @@ -56,7 +56,7 @@ Throws an error if there is already a tuple with the same primary key. ... conn.insert('demo', ('BBBB', 'Bravo')) ... except Exception as exc: ... print(exc) - ... + ... (3, 'Duplicate key exists in unique index "pk" in space "demo" with old tuple - ["BBBB", "Bravo"] and new tuple - ["BBBB", "Bravo"]') Replace @@ -125,7 +125,7 @@ Creating a space instance An instance of :class:`~tarantool.space.Space` is a named object to access the key space. -Create a ``demo`` object that will be used to access the space +Create a ``demo`` object that will be used to access the space with id ``'demo'``: .. code-block:: python @@ -205,26 +205,26 @@ read-write and read-only pool instances: Receiving out-of-band messages ---------------------------------- -Receiving out-of-band messages from a server that uses box.session.push -call is supported for methods: :meth:`~tarantool.Connection.call`, -:meth:`~tarantool.Connection.eval`, :meth:`~tarantool.Connection.select`, -:meth:`~tarantool.Connection.insert`, :meth:`~tarantool.Connection.replace`, -:meth:`~tarantool.Connection.update`, :meth:`~tarantool.Connection.upsert`, +Receiving out-of-band messages from a server that uses box.session.push +call is supported for methods: :meth:`~tarantool.Connection.call`, +:meth:`~tarantool.Connection.eval`, :meth:`~tarantool.Connection.select`, +:meth:`~tarantool.Connection.insert`, :meth:`~tarantool.Connection.replace`, +:meth:`~tarantool.Connection.update`, :meth:`~tarantool.Connection.upsert`, :meth:`~tarantool.Connection.delete`. -To work with out-of-band messages, 2 optional arguments are used in +To work with out-of-band messages, 2 optional arguments are used in the methods listed above: * `on_push` - callback, launched with the received data for each out-of-band message. Two arguments for this callback are expected: - + * the first is the received from an out-of-band message data. * the second is `on_push_ctx`, variable for working with callback context (for example, recording the result or pass data to callback). * `on_push_ctx` - result of the `on_push` work can be written to this variable, or through this variable you can pass data to `on_push` callback. -Below is an example of the proposed API with method :meth:`~tarantool.Connection.call` -and :meth:`~tarantool.Connection.insert`. In the described example, before the end -of the :meth:`~tarantool.Connection.call` and :meth:`~tarantool.Connection.insert`, +Below is an example of the proposed API with method :meth:`~tarantool.Connection.call` +and :meth:`~tarantool.Connection.insert`. In the described example, before the end +of the :meth:`~tarantool.Connection.call` and :meth:`~tarantool.Connection.insert`, out-of-band messages are processed via specified callback. In the example below, two shells are used, in the first we will configure the server: @@ -249,7 +249,7 @@ In the example below, two shells are used, in the first we will configure the se return x end -In the second shell, we will execute a :meth:`~tarantool.Connection.call` +In the second shell, we will execute a :meth:`~tarantool.Connection.call` with receiving out-of-band messages from the server: .. code-block:: python @@ -266,11 +266,11 @@ with receiving out-of-band messages from the server: conn = tarantool.Connection(port=3301) res = conn.call( 'server_function', - on_push=callback, + on_push=callback, on_push_ctx=callback_res ) - # receiving out-of-band messages, + # receiving out-of-band messages, # the conn.call is not finished yet. >>> run callback with data: [[1, 0]] @@ -285,7 +285,7 @@ with receiving out-of-band messages from the server: print(callback_res) >>> [[[1, 1]], [[2, 1]], [[3, 1]]] -Let's go back to the first shell with the server and +Let's go back to the first shell with the server and create a space and a trigger for it: .. code-block:: lua @@ -315,7 +315,7 @@ create a space and a trigger for it: on_replace_callback ) -Now, in the second shell, we will execute an :meth:`~tarantool.ConnectionPool.insert` +Now, in the second shell, we will execute an :meth:`~tarantool.ConnectionPool.insert` with out-of-band message processing: .. code-block:: python @@ -333,7 +333,7 @@ with out-of-band message processing: on_push_ctx=callback_res, ) - # receiving out-of-band messages, + # receiving out-of-band messages, # the conn_pool.insert is not finished yet. >>> run callback with data: [[100, 0]] @@ -352,7 +352,7 @@ with out-of-band message processing: Interaction with the crud module ---------------------------------- -Through the :class:`~tarantool.Connection` object, you can access +Through the :class:`~tarantool.Connection` object, you can access `crud module `_ methods: .. code-block:: python @@ -362,16 +362,16 @@ Through the :class:`~tarantool.Connection` object, you can access >>> conn = tarantool.Connection(host='localhost',port=3301,fetch_schema=False) >>> conn.crud_ - conn.crud_count( conn.crud_insert( conn.crud_insert_object_many( - conn.crud_min( conn.crud_replace_object( conn.crud_stats( - conn.crud_unflatten_rows( conn.crud_upsert_many( conn.crud_delete( - conn.crud_insert_many( conn.crud_len( conn.crud_replace( - conn.crud_replace_object_many( conn.crud_storage_info( conn.crud_update( - conn.crud_upsert_object( conn.crud_get( conn.crud_insert_object( - conn.crud_max( conn.crud_replace_many( conn.crud_select( + conn.crud_count( conn.crud_insert( conn.crud_insert_object_many( + conn.crud_min( conn.crud_replace_object( conn.crud_stats( + conn.crud_unflatten_rows( conn.crud_upsert_many( conn.crud_delete( + conn.crud_insert_many( conn.crud_len( conn.crud_replace( + conn.crud_replace_object_many( conn.crud_storage_info( conn.crud_update( + conn.crud_upsert_object( conn.crud_get( conn.crud_insert_object( + conn.crud_max( conn.crud_replace_many( conn.crud_select( conn.crud_truncate( conn.crud_upsert( conn.crud_upsert_object_many( -As an example, consider :meth:`~tarantool.Connection.crud_insert` and :meth:`~tarantool.Connection.crud_insert_object_many`. +As an example, consider :meth:`~tarantool.Connection.crud_insert` and :meth:`~tarantool.Connection.crud_insert_object_many`. It is recommended to enclose calls in the try-except construction as follows: .. code-block:: python @@ -392,13 +392,13 @@ It is recommended to enclose calls in the try-except construction as follows: ... res = conn.crud_insert('tester', (3500,300,'Rob')) ... except CrudModuleError as e: ... exc_crud = e - ... + ... >>> exc_crud CrudModuleError(0, 'Failed to insert: Duplicate key exists in unique index "primary_index" in space "tester" with old tuple - [3500, 300, "Rob"] and new tuple - [3500, 300, "Rob"]') >>> exc_crud.extra_info_error >>> exc_crud.extra_info_error. - exc_crud.extra_info_error.class_name exc_crud.extra_info_error.err exc_crud.extra_info_error.file exc_crud.extra_info_error.line exc_crud.extra_info_error.str + exc_crud.extra_info_error.class_name exc_crud.extra_info_error.err exc_crud.extra_info_error.file exc_crud.extra_info_error.line exc_crud.extra_info_error.str >>> exc_crud.extra_info_error.class_name 'InsertError' >>> exc_crud.extra_info_error.str @@ -409,7 +409,7 @@ It is recommended to enclose calls in the try-except construction as follows: ... res = conn.crud_insert_object_many('tester', ({'id':3,'bucket_id':100,'name':'Ann'}, {'id':4,'bucket_id':100,'name':'Sam'}), {'timeout':100, 'rollback_on_error':False}) ... except CrudModuleManyError as e: ... exc_crud = e - ... + ... >>> exc_crud CrudModuleManyError(0, 'Got multiple errors, see errors_list') >>> exc_crud.success_list # some of the rows were inserted. @@ -422,7 +422,7 @@ It is recommended to enclose calls in the try-except construction as follows: 'CallError: Failed for 037adb3a-b9e3-4f78-a6d1-9f0cdb6cbefc: Function returned an error: Duplicate key exists in unique index "primary_index" in space "tester" with old tuple - [3500, 300, "Rob"] and new tuple - [3500, 100, "Mike"]' >>> exc_crud.errors_list[1].str 'InsertManyError: Failed to flatten object: FlattenError: Object is specified in bad format: FlattenError: Unknown field "second_name" is specified' - + # If there are no problems with any rows, the entire response will be contained in the res variable. >>> res = conn.crud_insert_object_many('tester', ({'id':3,'bucket_id':100,'name':'Ann'}, {'id':4,'bucket_id':100,'name':'Sam'}), {'timeout':100, 'rollback_on_error':False}) >>> res.rows @@ -436,7 +436,7 @@ If module crud not found on the router or user has not sufficient grants: ... res = conn.crud_insert('tester', (22221,300,'Rob')) ... except DatabaseError as e: ... exc_db = e - ... + ... >>> exc_db DatabaseError(33, "Procedure 'crud.insert' is not defined. Ensure that you're calling crud.router and user has sufficient grants") >>> exc_db.extra_info diff --git a/tarantool/connection.py b/tarantool/connection.py index d38c4cc0..8ec7645c 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -216,7 +216,7 @@ def delete(self, space_name, key, *, index=None, on_push=None, on_push_ctx=None) raise NotImplementedError @abc.abstractmethod - def upsert(self, space_name, tuple_value, op_list, *, index=None, + def upsert(self, space_name, tuple_value, op_list, *, index=None, on_push=None, on_push_ctx=None): """ Reference implementation: :meth:`~tarantool.Connection.upsert`. @@ -1141,7 +1141,7 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): continue while response._code == IPROTO_CHUNK: - if on_push is not None: + if on_push is not None: on_push(response._data, on_push_ctx) response = request.response_class(self, self._read_response()) @@ -1999,7 +1999,7 @@ def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, i :param on_push_ctx: Сontext for working with on_push callback. :type on_push_ctx: optional - + :rtype: :class:`~tarantool.response.Response` :raise: :exc:`~AssertionError`, @@ -2167,7 +2167,7 @@ def _unpacker_factory(self): def crud_insert(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Inserts row through the + Inserts row through the `crud `__. :param space_name: The name of the target space. @@ -2198,7 +2198,7 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], opts: dict={} def crud_insert_object(self, space_name: str, values: dict, opts: dict={}) -> CrudResult: """ - Inserts object row through the + Inserts object row through the `crud `__. :param space_name: The name of the target space. @@ -2229,7 +2229,7 @@ def crud_insert_object(self, space_name: str, values: dict, opts: dict={}) -> Cr def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Inserts batch rows through the + Inserts batch rows through the `crud `__. :param space_name: The name of the target space. @@ -2267,7 +2267,7 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: di def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Inserts batch object rows through the + Inserts batch object rows through the `crud `__. :param space_name: The name of the target space. @@ -2305,7 +2305,7 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], o def crud_get(self, space_name: str, key: int, opts: dict={}) -> CrudResult: """ - Gets row through the + Gets row through the `crud `__. :param space_name: The name of the target space. @@ -2335,7 +2335,7 @@ def crud_get(self, space_name: str, key: int, opts: dict={}) -> CrudResult: def crud_update(self, space_name: str, key: int, operations: list=[], opts: dict={}) -> CrudResult: """ - Updates row through the + Updates row through the `crud `__. :param space_name: The name of the target space. @@ -2369,7 +2369,7 @@ def crud_update(self, space_name: str, key: int, operations: list=[], opts: dict def crud_delete(self, space_name: str, key: int, opts: dict={}) -> CrudResult: """ - Deletes row through the + Deletes row through the `crud `__. :param space_name: The name of the target space. @@ -2399,7 +2399,7 @@ def crud_delete(self, space_name: str, key: int, opts: dict={}) -> CrudResult: def crud_replace(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Replaces row through the + Replaces row through the `crud `__. :param space_name: The name of the target space. @@ -2430,7 +2430,7 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], opts: dict={ def crud_replace_object(self, space_name: str, values: dict, opts: dict={}) -> CrudResult: """ - Replaces object row through the + Replaces object row through the `crud `__. :param space_name: The name of the target space. @@ -2461,7 +2461,7 @@ def crud_replace_object(self, space_name: str, values: dict, opts: dict={}) -> C def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Replaces batch rows through the + Replaces batch rows through the `crud `__. :param space_name: The name of the target space. @@ -2499,7 +2499,7 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: d def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Replaces batch object rows through the + Replaces batch object rows through the `crud `__. :param space_name: The name of the target space. @@ -2537,7 +2537,7 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: list=[], opts: dict={}) -> CrudResult: """ - Upserts row through the + Upserts row through the `crud `__. :param space_name: The name of the target space. @@ -2572,7 +2572,7 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: l def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], opts: dict={}) -> CrudResult: """ - Upserts object row through the + Upserts object row through the `crud `__. :param space_name: The name of the target space. @@ -2599,7 +2599,7 @@ def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.upsert_object", space_name, values, operations, opts) - + if crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) @@ -2607,7 +2607,7 @@ def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Upserts batch rows through the + Upserts batch rows through the `crud `__. :param space_name: The name of the target space. @@ -2645,7 +2645,7 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], opts: dict={}) -> CrudResult: """ - Upserts batch object rows through the + Upserts batch object rows through the `crud `__. :param space_name: The name of the target space. @@ -2683,7 +2683,7 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple def crud_select(self, space_name: str, conditions: list=[], opts: dict={}) -> CrudResult: """ - Selects rows through the + Selects rows through the `crud `__. :param space_name: The name of the target space. @@ -2714,7 +2714,7 @@ def crud_select(self, space_name: str, conditions: list=[], opts: dict={}) -> Cr def crud_min(self, space_name: str, index_name: str, opts: dict={}) -> CrudResult: """ - Gets rows with minimum value in the specified index through + Gets rows with minimum value in the specified index through the `crud `__. :param space_name: The name of the target space. @@ -2744,7 +2744,7 @@ def crud_min(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul def crud_max(self, space_name: str, index_name: str, opts: dict={}) -> CrudResult: """ - Gets rows with maximum value in the specified index through + Gets rows with maximum value in the specified index through the `crud `__. :param space_name: The name of the target space. @@ -2774,7 +2774,7 @@ def crud_max(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul def crud_truncate(self, space_name: str, opts: dict={}) -> bool: """ - Truncate rows through + Truncate rows through the `crud `__. :param space_name: The name of the target space. @@ -2794,16 +2794,16 @@ def crud_truncate(self, space_name: str, opts: dict={}) -> bool: crud_resp = call_crud(self, "crud.truncate", space_name, opts) - # In absence of an error, crud does not give + # In absence of an error, crud does not give # variable err as nil (as in most cases). if len(crud_resp) != 1: raise CrudModuleError(None, CrudError(crud_resp[1])) return crud_resp[0] - + def crud_len(self, space_name: str, opts: dict={}) -> int: """ - Gets the number of tuples in the space through + Gets the number of tuples in the space through the `crud `__. :param space_name: The name of the target space. @@ -2823,7 +2823,7 @@ def crud_len(self, space_name: str, opts: dict={}) -> int: crud_resp = call_crud(self, "crud.len", space_name, opts) - # In absence of an error, crud does not give + # In absence of an error, crud does not give # variable err as nil (as in most cases). if len(crud_resp) != 1: raise CrudModuleError(None, CrudError(crud_resp[1])) @@ -2832,7 +2832,7 @@ def crud_len(self, space_name: str, opts: dict={}) -> int: def crud_storage_info(self, opts: dict={}) -> dict: """ - Gets storages status through the + Gets storages status through the `crud `__. :param opts: The opts for the crud module. @@ -2848,7 +2848,7 @@ def crud_storage_info(self, opts: dict={}) -> dict: crud_resp = call_crud(self, "crud.storage_info", opts) - # In absence of an error, crud does not give + # In absence of an error, crud does not give # variable err as nil (as in most cases). if len(crud_resp) != 1: raise CrudModuleError(None, CrudError(crud_resp[1])) @@ -2857,7 +2857,7 @@ def crud_storage_info(self, opts: dict={}) -> dict: def crud_count(self, space_name: str, conditions: list=[], opts: dict={}) -> int: """ - Gets rows count through the + Gets rows count through the `crud `__. :param space_name: The name of the target space. @@ -2888,7 +2888,7 @@ def crud_count(self, space_name: str, conditions: list=[], opts: dict={}) -> int def crud_stats(self, space_name: str=None) -> CrudResult: """ - Gets statistics from the + Gets statistics from the `crud `__. :param space_name: The name of the target space. @@ -2912,7 +2912,7 @@ def crud_stats(self, space_name: str=None) -> CrudResult: def crud_unflatten_rows(self, rows: list, metadata: list) -> list: """ - Makes rows unflatten through the + Makes rows unflatten through the `crud `__. :param rows: The rows to unflatten. diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index b5e5f4f4..fd9f9a1e 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -945,7 +945,7 @@ def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, on_push=Non .. _update: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/update/ """ - return self._send(mode, 'update', space_name, key, + return self._send(mode, 'update', space_name, key, op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) def ping(self, notime=False, *, mode=None): @@ -1041,8 +1041,8 @@ def execute(self, query, params=None, *, mode=None): def crud_insert(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_insert request on the pool server: - inserts row through the + Execute an crud_insert request on the pool server: + inserts row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_insert`. @@ -1068,8 +1068,8 @@ def crud_insert(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_insert_object(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_insert_object request on the pool server: - inserts object row through the + Execute an crud_insert_object request on the pool server: + inserts object row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_insert_object`. @@ -1095,8 +1095,8 @@ def crud_insert_object(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_insert_many(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_insert_many request on the pool server: - inserts batch rows through the + Execute an crud_insert_many request on the pool server: + inserts batch rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_insert_many`. @@ -1122,7 +1122,7 @@ def crud_insert_many(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_insert_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_insert_object_many request on the pool server: + Execute an crud_insert_object_many request on the pool server: inserts batch object rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_insert_object_many`. @@ -1149,8 +1149,8 @@ def crud_insert_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY) def crud_get(self, space_name, key, opts={}, *, mode=Mode.ANY): """ - Execute an crud_get request on the pool server: - gets row through the + Execute an crud_get request on the pool server: + gets row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_get`. @@ -1176,8 +1176,8 @@ def crud_get(self, space_name, key, opts={}, *, mode=Mode.ANY): def crud_update(self, space_name, key, operations=[], opts={}, *, mode=Mode.ANY): """ - Execute an crud_update request on the pool server: - updates row through the + Execute an crud_update request on the pool server: + updates row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_update`. @@ -1206,8 +1206,8 @@ def crud_update(self, space_name, key, operations=[], opts={}, *, mode=Mode.ANY) def crud_delete(self, space_name, key, opts={}, *, mode=Mode.ANY): """ - Execute an crud_delete request on the pool server: - deletes row through the + Execute an crud_delete request on the pool server: + deletes row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_delete`. @@ -1233,8 +1233,8 @@ def crud_delete(self, space_name, key, opts={}, *, mode=Mode.ANY): def crud_replace(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_replace request on the pool server: - replaces row through the + Execute an crud_replace request on the pool server: + replaces row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_replace`. @@ -1260,8 +1260,8 @@ def crud_replace(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_replace_object(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_replace_object request on the pool server: - replaces object row through the + Execute an crud_replace_object request on the pool server: + replaces object row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_replace_object`. @@ -1287,8 +1287,8 @@ def crud_replace_object(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_replace_many(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_replace_many request on the pool server: - replaces batch rows through the + Execute an crud_replace_many request on the pool server: + replaces batch rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_replace_many`. @@ -1314,8 +1314,8 @@ def crud_replace_many(self, space_name, values, opts={}, *, mode=Mode.ANY): def crud_replace_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY): """ - Execute an crud_replace_object_many request on the pool server: - replaces batch object rows through the + Execute an crud_replace_object_many request on the pool server: + replaces batch object rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_replace_object_many`. @@ -1341,8 +1341,8 @@ def crud_replace_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY def crud_upsert(self, space_name, values, operations=[], opts={}, *, mode=Mode.ANY): """ - Execute an crud_upsert request on the pool server: - upserts row through the + Execute an crud_upsert request on the pool server: + upserts row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_upsert`. @@ -1371,8 +1371,8 @@ def crud_upsert(self, space_name, values, operations=[], opts={}, *, mode=Mode.A def crud_upsert_object(self, space_name, values, operations=[], opts={}, *, mode=Mode.ANY): """ - Execute an crud_upsert_object request on the pool server: - upserts object row through the + Execute an crud_upsert_object request on the pool server: + upserts object row through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_upsert_object`. @@ -1401,8 +1401,8 @@ def crud_upsert_object(self, space_name, values, operations=[], opts={}, *, mode def crud_upsert_many(self, space_name, values_operation, opts={}, *, mode=Mode.ANY): """ - Execute an crud_upsert_many request on the pool server: - upserts batch rows through the + Execute an crud_upsert_many request on the pool server: + upserts batch rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_upsert_many`. @@ -1428,8 +1428,8 @@ def crud_upsert_many(self, space_name, values_operation, opts={}, *, mode=Mode.A def crud_upsert_object_many(self, space_name, values_operation, opts={}, *, mode=Mode.ANY): """ - Execute an crud_upsert_object_many request on the pool server: - upserts batch object rows through the + Execute an crud_upsert_object_many request on the pool server: + upserts batch object rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_upsert_object_many`. @@ -1455,8 +1455,8 @@ def crud_upsert_object_many(self, space_name, values_operation, opts={}, *, mode def crud_select(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): """ - Execute an crud_select request on the pool server: - selects rows through the + Execute an crud_select request on the pool server: + selects rows through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_select`. @@ -1482,8 +1482,8 @@ def crud_select(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): def crud_min(self, space_name, index_name, opts={}, *, mode=Mode.ANY): """ - Execute an crud_min request on the pool server: - gets rows with minimum value in the specified index through + Execute an crud_min request on the pool server: + gets rows with minimum value in the specified index through `crud `__. Refer to :meth:`~tarantool.Connection.crud_min`. @@ -1509,8 +1509,8 @@ def crud_min(self, space_name, index_name, opts={}, *, mode=Mode.ANY): def crud_max(self, space_name, index_name, opts={}, *, mode=Mode.ANY): """ - Execute an crud_max request on the pool server: - gets rows with maximum value in the specified index through + Execute an crud_max request on the pool server: + gets rows with maximum value in the specified index through `crud `__. Refer to :meth:`~tarantool.Connection.crud_max`. @@ -1536,8 +1536,8 @@ def crud_max(self, space_name, index_name, opts={}, *, mode=Mode.ANY): def crud_len(self, space_name, opts={}, *, mode=Mode.ANY): """ - Execute an crud_len request on the pool server: - gets the number of tuples in the space through + Execute an crud_len request on the pool server: + gets the number of tuples in the space through `crud `__. Refer to :meth:`~tarantool.Connection.crud_len`. @@ -1560,8 +1560,8 @@ def crud_len(self, space_name, opts={}, *, mode=Mode.ANY): def crud_storage_info(self, opts={}, *, mode=Mode.ANY): """ - Execute an crud_storage_info request on the pool server: - gets storages status through the + Execute an crud_storage_info request on the pool server: + gets storages status through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_storage_info`. @@ -1581,8 +1581,8 @@ def crud_storage_info(self, opts={}, *, mode=Mode.ANY): def crud_count(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): """ - Execute an crud_count request on the pool server: - gets rows count through the + Execute an crud_count request on the pool server: + gets rows count through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_count`. @@ -1608,8 +1608,8 @@ def crud_count(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): def crud_stats(self, space_name=None, *, mode=Mode.ANY): """ - Execute an crud_stats request on the pool server: - gets statistics through the + Execute an crud_stats request on the pool server: + gets statistics through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_stats`. @@ -1629,7 +1629,7 @@ def crud_stats(self, space_name=None, *, mode=Mode.ANY): def crud_unflatten_rows(self, rows, metadata, *, mode=Mode.ANY): """ - Makes rows unflatten through the + Makes rows unflatten through the `crud `__. Refer to :meth:`~tarantool.Connection.crud_unflatten_rows`. @@ -1652,8 +1652,8 @@ def crud_unflatten_rows(self, rows, metadata, *, mode=Mode.ANY): def crud_truncate(self, space_name, opts={}, *, mode=Mode.ANY): """ - Execute an crud_truncate request on the pool server: - truncates rows through + Execute an crud_truncate request on the pool server: + truncates rows through `crud `__. Refer to :meth:`~tarantool.Connection.crud_truncate`. diff --git a/tarantool/crud.py b/tarantool/crud.py index dee1be36..044271a9 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -9,7 +9,7 @@ class CrudResponse(object): """ - Contains response fields from the `crud`_ module that correspond + Contains response fields from the `crud`_ module that correspond to the Lua implementation. .. _crud: https://github.com/tarantool/crud/ @@ -35,14 +35,14 @@ def __init__(self, response): class CrudResult(CrudResponse): """ - Contains result's fields from result variable + Contains result's fields from result variable of crud module operation. """ class CrudError(CrudResponse): """ - Contains error's fields from error variable + Contains error's fields from error variable of crud module operation. """ diff --git a/tarantool/error.py b/tarantool/error.py index ecd09c8a..99fc7c7b 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -340,7 +340,7 @@ class CrudModuleError(DatabaseError): def __init__(self, _, error): """ Sets fields with result and errors. - + :param args: The tuple from the crud module with result and errors. :type args: :obj:`tuple` """ @@ -361,7 +361,7 @@ class CrudModuleManyError(DatabaseError): def __init__(self, success, error): """ Sets fields with result and errors. - + :param args: The tuple from the crud module with result and errors. :type args: :obj:`tuple` """ diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index bad947fb..3ffd59b7 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -263,7 +263,7 @@ def encode(obj, _): bytes_reverted.append(get_mp_sign(sign)) digit_count = 0 - # We need to update the scale after possible strip_decimal_str() + # We need to update the scale after possible strip_decimal_str() scale = 0 for i in range(len(str_repr) - 1, first_digit_ind - 1, -1): @@ -348,7 +348,7 @@ def decode(data, _): :raise: :exc:`~tarantool.error.MsgpackError` """ - scale = data[0] + scale = data[0] sign = get_str_sign(data[-1] & 0x0f) diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index e90ff0a0..0ccb6f0e 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -90,7 +90,7 @@ def __init__(self, *, year=0, month=0, week=0, :meth:`~tarantool.Datetime.__add__`. :type adjust: :class:`~tarantool.IntervalAdjust`, optional """ - + self.year = year self.month = month self.week = week diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 4af62d90..90281a65 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -32,12 +32,12 @@ def setUpClass(self): def setUp(self): time.sleep(1) # Open connections to instance. - self.conn = tarantool.Connection(host=self.host, port=self.port, + self.conn = tarantool.Connection(host=self.host, port=self.port, user='guest', password='', fetch_schema=False) - self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port, + self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port, user='guest', password='', fetch_schema=False) - self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], - user='guest', password='', + self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], + user='guest', password='', fetch_schema=False) # Time for vshard group configuration. time.sleep(1) @@ -93,7 +93,7 @@ def setUp(self): 'success': { 'input': { 'args': [ - 'tester', + 'tester', [ [3, 100, 'Jacob'], [4, 100, 'Wyatt'], @@ -115,7 +115,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ [3, 100, 'Julian'], [4, 100, 'Hudson'], @@ -137,7 +137,7 @@ def setUp(self): 'success': { 'input': { 'args': [ - 'tester', + 'tester', [ {'id': 9, 'bucket_id': 100, 'name': 'Sharar'}, {'id': 10, 'bucket_id': 100, 'name': 'Thaddeus'}, @@ -159,7 +159,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ {'id': 9, 'bucket_id': 100, 'name': 'Silvanus'}, {'id': 10, 'bucket_id': 100, 'name': 'Timeus'}, @@ -192,7 +192,7 @@ def setUp(self): }, 'output': { 'str': [ - r'GetError: Space "no-such-space-name" doesn\'t exist', + r'GetError: Space "no-such-space-name" doesn\'t exist', ], }, }, @@ -212,7 +212,7 @@ def setUp(self): }, 'output': { 'str': [ - r"UpdateError", + r"UpdateError", ], }, }, @@ -232,7 +232,7 @@ def setUp(self): }, 'output': { 'str': [ - r'DeleteError: Space "no-such-space-name" doesn\'t exist', + r'DeleteError: Space "no-such-space-name" doesn\'t exist', ], }, }, @@ -281,7 +281,7 @@ def setUp(self): 'success': { 'input': { 'args': [ - 'tester', + 'tester', [ [2, 100, 'Cephus'], [3, 100, 'Esau'], @@ -303,7 +303,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ [3, 100, 'Ephron'], [4, 100, 'Ethan'], @@ -325,7 +325,7 @@ def setUp(self): 'success': { 'input': { 'args': [ - 'tester', + 'tester', [ {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, {'id': 3, 'bucket_id': 100, 'name': 'Esau'}, @@ -347,7 +347,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ {'id': 3, 'bucket_id': 100, 'name': 'Ephron'}, {'id': 4, 'bucket_id': 100, 'name': 'Ethan'}, @@ -380,7 +380,7 @@ def setUp(self): }, 'output': { 'str': [ - r"UpsertError", + r"UpsertError", ], }, }, @@ -388,7 +388,7 @@ def setUp(self): 'crud_upsert_object': { 'success': { 'input': { - 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, + 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, [['+', 'bucket_id', 1]], {'timeout': 10}], }, 'output': { @@ -397,7 +397,7 @@ def setUp(self): }, 'error': { 'input': { - 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, + 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, [['+', 'age', 1]], {'timeout': 10}], }, 'output': { @@ -426,7 +426,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ [[3, 100, 'Ephron'], [['+', 'bucket_id', 1]]], [[4, 100, 'Ethan'], [['+', 'bucket_id', 1]]], @@ -462,7 +462,7 @@ def setUp(self): 'error': { 'input': { 'args': [ - 'tester', + 'tester', [ [{'id': 3, 'bucket_id': 100, 'name': 'Ephron'}, [['+', 'bucket_id', 1]]], [{'id': 4, 'bucket_id': 100, 'name': 'Ethan'}, [['+', 'bucket_id', 1]]], @@ -494,7 +494,7 @@ def setUp(self): }, 'output': { 'str': [ - r'SelectError: Space "no-such-space-name" doesn\'t exist', + r'SelectError: Space "no-such-space-name" doesn\'t exist', ], }, }, @@ -514,7 +514,7 @@ def setUp(self): }, 'output': { 'str': [ - r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', + r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', ], }, }, @@ -534,7 +534,7 @@ def setUp(self): }, 'output': { 'str': [ - r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', + r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', ], }, }, @@ -554,7 +554,7 @@ def setUp(self): }, 'output': { 'str': [ - r'LenError: Space "no-such-space-name" doesn\'t exist', + r'LenError: Space "no-such-space-name" doesn\'t exist', ], }, }, @@ -574,7 +574,7 @@ def setUp(self): }, 'output': { 'str': [ - r'CountError: Space "no-such-space-name" doesn\'t exist', + r'CountError: Space "no-such-space-name" doesn\'t exist', ], }, }, @@ -584,27 +584,27 @@ def setUp(self): 'input': { 'args': [ [ - [1, 100, 'Mike'], - [2, 100, 'Mike'], - [3, 100, 'Mike'], - [4, 100, 'Mike'], - [5, 200, 'Bill'], + [1, 100, 'Mike'], + [2, 100, 'Mike'], + [3, 100, 'Mike'], + [4, 100, 'Mike'], + [5, 200, 'Bill'], [6, 300, 'Rob'], ], [ - {'name': 'id', 'type': 'unsigned'}, - {'name': 'bucket_id', 'type': 'unsigned'}, + {'name': 'id', 'type': 'unsigned'}, + {'name': 'bucket_id', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'} ], ], }, 'output': { 'scalar': [ - {'bucket_id': 100, 'name': 'Mike', 'id': 1}, - {'bucket_id': 100, 'name': 'Mike', 'id': 2}, - {'bucket_id': 100, 'name': 'Mike', 'id': 3}, - {'bucket_id': 100, 'name': 'Mike', 'id': 4}, - {'bucket_id': 200, 'name': 'Bill', 'id': 5}, + {'bucket_id': 100, 'name': 'Mike', 'id': 1}, + {'bucket_id': 100, 'name': 'Mike', 'id': 2}, + {'bucket_id': 100, 'name': 'Mike', 'id': 3}, + {'bucket_id': 100, 'name': 'Mike', 'id': 4}, + {'bucket_id': 200, 'name': 'Bill', 'id': 5}, {'bucket_id': 300, 'name': 'Rob', 'id': 6}, ], }, @@ -633,7 +633,7 @@ def setUp(self): }, 'output': { 'str': [ - r'"no-such-space-name" doesn\'t exist', + r'"no-such-space-name" doesn\'t exist', ], }, }, @@ -684,8 +684,8 @@ def _correct_operation_with_crud(self, testing_function, case, mode=None): if 'operations' in case['success']['output']: # Case for statistics testing. for operation in case['success']['output']['operations']: - self.assertEqual(operation in resp.__dict__, True, - 'Problem with finding a field with a statistic about operation ' + self.assertEqual(operation in resp.__dict__, True, + 'Problem with finding a field with a statistic about operation ' + operation) def _exception_operation_with_crud(self, testing_function, case, mode=None): diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index fb2d2855..e705ec74 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -315,7 +315,7 @@ def test_tarantool_encode(self): lua_eval = f""" local dt = {case['tarantool']} - + local tuple = box.space['test']:get('{name}') assert(tuple ~= nil) diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 0df06337..5e35bb61 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -131,12 +131,12 @@ def test_description(self): def test_ExceptionsAsConnectionAttributes(self): # Workaround for https://github.com/baztian/dbapi-compliance/issues/5 - # OPTIONAL EXTENSION - # Test for the optional DB API 2.0 extension, where the exceptions - # are exposed as attributes on the Connection object - # I figure this optional extension will be implemented by any - # driver author who is using this test suite, so it is enabled - # by default. + # OPTIONAL EXTENSION + # Test for the optional DB API 2.0 extension, where the exceptions + # are exposed as attributes on the Connection object + # I figure this optional extension will be implemented by any + # driver author who is using this test suite, so it is enabled + # by default. drv = self.driver con = self._connect() try: diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 8029d7b9..e64e6b54 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -398,7 +398,7 @@ def test_msgpack_encode_with_precision_loss(self): msgpack.ExtType(code=1, data=case['msgpack']) ) ) - + @skip_or_run_decimal_test def test_tarantool_encode_with_precision_loss(self): diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 89f5c35f..abf8d93d 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -78,7 +78,7 @@ def setUp(self): # msgpack data for different encodings are actually the same, # but sometimes python msgpack module use different string - # types (str8 and str16) for the same strings depending on use_bin_type: + # types (str8 and str16) for the same strings depending on use_bin_type: # # >>> msgpack.Packer(use_bin_type=True).pack('[string " local err = box.error.ne..."]') # b'\xd9;[string " local err = box.error.ne..."]' diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 2252ebe8..343c95fb 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -186,7 +186,7 @@ def test_tarantool_encode(self): lua_eval = f""" local interval = {case['tarantool']} - + local tuple = box.space['test']:get('{name}') assert(tuple ~= nil) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index f5e27f16..cfc7c562 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -177,7 +177,7 @@ def expect_PREFER_RW_iterate_through_all_instances_if_there_are_no_RW(): PREFER_RW_ports_result_all_ro.add(get_port(self, tarantool.Mode.PREFER_RW)) self.assertSetEqual(PREFER_RW_ports_result_all_ro, all_ports) - + self.retry(func=expect_PREFER_RW_iterate_through_all_instances_if_there_are_no_RW) # Setup cluster with no RO. diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 618a56f5..91d0f82e 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -87,11 +87,11 @@ def setUpClass(self): def setUp(self): # Open connection, connection pool and mesh connection to instance. - self.conn = tarantool.Connection(host=self.host, port=self.port, + self.conn = tarantool.Connection(host=self.host, port=self.port, user='test', password='test') - self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], + self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], user='test', password='test') - self.mesh_conn = tarantool.MeshConnection(host=self.host, port=self.port, + self.mesh_conn = tarantool.MeshConnection(host=self.host, port=self.port, user='test', password='test') push_test_cases = { diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 55460018..85deace1 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -78,12 +78,12 @@ def setUpClass(self): 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, + self.mesh_con_schema_disable = tarantool.MeshConnection(host=self.srv.host, port=self.srv.args['primary'], - fetch_schema=False, + fetch_schema=False, user='test', password='test') - self.pool_con_schema_disable = tarantool.ConnectionPool([{'host':self.srv.host, - 'port':self.srv.args['primary']}], + 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 @@ -122,7 +122,7 @@ def setUpClass(self): 'constr_tester_2', { format = { { name = 'id', type = 'unsigned' }, - { name = 'table1_id', type = 'unsigned', + { name = 'table1_id', type = 'unsigned', foreign_key = { fk_video = { space = 'constr_tester_1', field = 'id' } }, }, { name = 'payload', type = 'number' }, @@ -419,7 +419,7 @@ def test_07_schema_version_update(self): 'insert': { 'input': ['tester', (1, None)], 'output': [[1, None]], - }, + }, 'upsert': { 'input': ['tester', (1, None), []], 'output': [], @@ -427,7 +427,7 @@ def test_07_schema_version_update(self): 'update': { 'input': ['tester', 1, []], 'output': [[1, None]], - }, + }, 'select': { 'input': ['tester', 1], 'output': [[1, None]], @@ -478,13 +478,13 @@ def _run_test_schema_fetch_disable(self, con, mode=None): try: if mode is not None: _ = testing_function( - *self.testing_methods['unavailable'][method_case]['input'], + *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 ' + + 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. @@ -493,7 +493,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): testing_function = getattr(con, method_case) if mode is not None: resp = testing_function( - *self.testing_methods['available'][method_case]['input'], + *self.testing_methods['available'][method_case]['input'], mode=mode) else: resp = testing_function( @@ -521,7 +521,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): testing_function = getattr(con, method_case) if mode is not None: resp = testing_function( - *self.testing_methods['unavailable'][method_case]['input'], + *self.testing_methods['unavailable'][method_case]['input'], mode=mode) else: resp = testing_function( @@ -530,7 +530,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): self.assertEqual(isinstance(resp, tarantool.space.Space), True) else: self.assertEqual( - resp.data, + resp.data, self.testing_methods['unavailable'][method_case]['output']) # Testing the schemaful connection with methods # that should have remained available. @@ -539,7 +539,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): testing_function = getattr(con, method_case) if mode is not None: resp = testing_function( - *self.testing_methods['available'][method_case]['input'], + *self.testing_methods['available'][method_case]['input'], mode=mode) else: resp = testing_function( @@ -548,12 +548,12 @@ def _run_test_schema_fetch_disable(self, con, mode=None): self.assertEqual(isinstance(resp, float), True) else: self.assertEqual( - resp.data, + 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: + else: self.assertNotEqual(con.schema_version, 1) self.assertNotEqual(con.schema, None) From 3a9aeb2509e25e0024ae45a1ae670965ab020e9e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:23:04 +0300 Subject: [PATCH 020/190] pylint: fix invalid-name cases Fix several cases of C0103 invalid-name. Part of #270 --- setup.py | 18 ++--- tarantool/connection.py | 72 +++++++++---------- tarantool/connection_pool.py | 92 ++++++++++++------------- tarantool/crud.py | 6 +- tarantool/error.py | 6 +- tarantool/mesh_connection.py | 28 ++++---- tarantool/msgpack_ext/datetime.py | 6 +- tarantool/msgpack_ext/interval.py | 4 +- tarantool/msgpack_ext/types/datetime.py | 4 +- tarantool/schema.py | 50 +++++++------- tarantool/utils.py | 4 +- test/setup_command.py | 2 +- test/suites/lib/skip.py | 22 +++--- test/suites/lib/tarantool_admin.py | 2 +- test/suites/lib/tarantool_server.py | 4 +- test/suites/test_connection.py | 2 +- test/suites/test_crud.py | 16 ++--- test/suites/test_datetime.py | 64 ++++++++--------- test/suites/test_dbapi.py | 2 +- test/suites/test_decimal.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_encoding.py | 6 +- test/suites/test_error_ext.py | 2 +- test/suites/test_execute.py | 2 +- test/suites/test_interval.py | 4 +- test/suites/test_mesh.py | 16 ++--- test/suites/test_package.py | 2 +- test/suites/test_pool.py | 76 ++++++++++---------- test/suites/test_protocol.py | 2 +- test/suites/test_push.py | 2 +- test/suites/test_reconnect.py | 2 +- test/suites/test_schema.py | 16 ++--- test/suites/test_ssl.py | 2 +- test/suites/test_uuid.py | 10 +-- 34 files changed, 275 insertions(+), 275 deletions(-) diff --git a/setup.py b/setup.py index 1c565e79..ece0e140 100755 --- a/setup.py +++ b/setup.py @@ -47,25 +47,25 @@ def run(self): # Test runner # python setup.py test try: - from test.setup_command import test - cmdclass["test"] = test + from test.setup_command import Test + cmdclass["test"] = Test except ImportError: pass def read(*parts): filename = os.path.join(os.path.dirname(__file__), *parts) - with codecs.open(filename, encoding='utf-8') as fp: - return fp.read() + with codecs.open(filename, encoding='utf-8') as file: + return file.read() -def get_dependencies(file): +def get_dependencies(filename): root = os.path.dirname(os.path.realpath(__file__)) - requirements = os.path.join(root, file) + requirements = os.path.join(root, filename) result = [] if os.path.isfile(requirements): - with open(requirements) as f: - return f.read().splitlines() - raise RuntimeError("Unable to get dependencies from file " + file) + with open(requirements) as file: + return file.read().splitlines() + raise RuntimeError("Unable to get dependencies from file " + filename) def find_version(*file_paths): version_file = read(*file_paths) diff --git a/tarantool/connection.py b/tarantool/connection.py index 8ec7645c..96cd9a4c 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -8,9 +8,9 @@ import socket try: import ssl - is_ssl_supported = True + IS_SSL_SUPPORTED = True except ImportError: - is_ssl_supported = False + IS_SSL_SUPPORTED = False import sys import abc @@ -109,6 +109,9 @@ ) from typing import Union +WWSAEWOULDBLOCK = 10035 +ER_UNKNOWN_REQUEST_TYPE = 48 + # Based on https://realpython.com/python-interface/ class ConnectionInterface(metaclass=abc.ABCMeta): """ @@ -862,9 +865,9 @@ def connect_tcp(self): (self.host, self.port), timeout=self.connection_timeout) self._socket.settimeout(self.socket_timeout) self._socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) - except socket.error as e: + except socket.error as exc: self.connected = False - raise NetworkError(e) + raise NetworkError(exc) def connect_unix(self): """ @@ -885,9 +888,9 @@ def connect_unix(self): self._socket.settimeout(self.connection_timeout) self._socket.connect(self.port) self._socket.settimeout(self.socket_timeout) - except socket.error as e: + except socket.error as exc: self.connected = False - raise NetworkError(e) + raise NetworkError(exc) def wrap_socket_ssl(self): """ @@ -898,7 +901,7 @@ def wrap_socket_ssl(self): :meta private: """ - if not is_ssl_supported: + if not IS_SSL_SUPPORTED: raise SslError("Your version of Python doesn't support SSL") ver = sys.version_info @@ -939,10 +942,10 @@ def wrap_socket_ssl(self): context.set_ciphers(self.ssl_ciphers) self._socket = context.wrap_socket(self._socket) - except SslError as e: - raise e - except Exception as e: - raise SslError(e) + except SslError as exc: + raise exc + except Exception as exc: + raise SslError(exc) def _ssl_load_cert_chain(self, context): """ @@ -967,8 +970,8 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=self.ssl_password) return - except Exception as e: - exc_list.append(e) + except Exception as exc: + exc_list.append(exc) if self.ssl_password_file is not None: @@ -979,8 +982,8 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=line.rstrip()) return - except Exception as e: - exc_list.append(e) + except Exception as exc: + exc_list.append(exc) try: @@ -993,8 +996,8 @@ def password_raise_error(): password=password_raise_error) return - except Exception as e: - exc_list.append(e) + except Exception as exc: + exc_list.append(exc) raise SslError(exc_list) @@ -1042,11 +1045,11 @@ def connect(self): self.load_schema() else: self.schema = None - except SslError as e: - raise e - except Exception as e: + except SslError as exc: + raise exc + except Exception as exc: self.connected = False - raise NetworkError(e) + raise NetworkError(exc) def _recv(self, to_read): """ @@ -1135,9 +1138,9 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): self._socket.sendall(bytes(request)) response = request.response_class(self, self._read_response()) break - except SchemaReloadException as e: + except SchemaReloadException as exc: if self.schema is not None: - self.update_schema(e.schema_version) + self.update_schema(exc.schema_version) continue while response._code == IPROTO_CHUNK: @@ -1166,8 +1169,8 @@ def check(): # Check that connection is alive buf = ctypes.create_string_buffer(2) try: sock_fd = self._socket.fileno() - except socket.error as e: - if e.errno == errno.EBADF: + except socket.error as exc: + if exc.errno == errno.EBADF: return errno.ECONNRESET else: if os.name == 'nt': @@ -1184,8 +1187,6 @@ def check(): # Check that connection is alive err = ctypes.get_last_error() self._socket.setblocking(True) - - WWSAEWOULDBLOCK = 10035 if (retbytes < 0) and (err == errno.EAGAIN or err == errno.EWOULDBLOCK or err == WWSAEWOULDBLOCK): @@ -1537,11 +1538,11 @@ class JoinState: def _ops_process(self, space, update_ops): new_ops = [] - for op in update_ops: - if isinstance(op[1], str): - op = list(op) - op[1] = self.schema.get_field(space, op[1])['id'] - new_ops.append(op) + for operation in update_ops: + if isinstance(operation[1], str): + operation = list(operation) + operation[1] = self.schema.get_field(space, operation[1])['id'] + new_ops.append(operation) return new_ops def join(self, server_uuid): @@ -1857,13 +1858,13 @@ def ping(self, notime=False): """ request = RequestPing(self) - t0 = time.time() + start_time = time.time() self._send_request(request) - t1 = time.time() + finish_time = time.time() if notime: return "Success" - return t1 - t0 + return finish_time - start_time def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, iterator=None, on_push=None, on_push_ctx=None): """ @@ -2140,7 +2141,6 @@ def _check_features(self): server_features = response.features server_auth_type = response.auth_type except DatabaseError as exc: - ER_UNKNOWN_REQUEST_TYPE = 48 if exc.code == ER_UNKNOWN_REQUEST_TYPE: server_protocol_version = None server_features = [] diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index fd9f9a1e..44102c57 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -93,13 +93,13 @@ class InstanceState(): """ :type: :class:`~tarantool.connection_pool.Status` """ - ro: typing.Optional[bool] = None + read_only: typing.Optional[bool] = None """ :type: :obj:`bool`, optional """ -def QueueFactory(): +def queue_factory(): """ Build a queue-based channel. """ @@ -126,14 +126,14 @@ class PoolUnit(): :type: :class:`~tarantool.Connection` """ - input_queue: queue.Queue = field(default_factory=QueueFactory) + input_queue: queue.Queue = field(default_factory=queue_factory) """ Channel to pass requests for the server thread. :type: :obj:`queue.Queue` """ - output_queue: queue.Queue = field(default_factory=QueueFactory) + output_queue: queue.Queue = field(default_factory=queue_factory) """ Channel to receive responses from the server thread. @@ -216,9 +216,9 @@ def __init__(self, pool): :class:`~tarantool.connection_pool.PoolUnit` objects """ - self.ANY_iter = None - self.RW_iter = None - self.RO_iter = None + self.any_iter = None + self.rw_iter = None + self.ro_iter = None self.pool = pool self.rebuild_needed = True @@ -228,9 +228,9 @@ def build(self): based on `box.info.ro`_ state. """ - ANY_pool = [] - RW_pool = [] - RO_pool = [] + any_pool = [] + rw_pool = [] + ro_pool = [] for key in self.pool: state = self.pool[key].state @@ -238,27 +238,27 @@ def build(self): if state.status == Status.UNHEALTHY: continue - ANY_pool.append(key) + any_pool.append(key) - if state.ro == False: - RW_pool.append(key) + if state.read_only == False: + rw_pool.append(key) else: - RO_pool.append(key) + ro_pool.append(key) - if len(ANY_pool) > 0: - self.ANY_iter = itertools.cycle(ANY_pool) + if len(any_pool) > 0: + self.any_iter = itertools.cycle(any_pool) else: - self.ANY_iter = None + self.any_iter = None - if len(RW_pool) > 0: - self.RW_iter = itertools.cycle(RW_pool) + if len(rw_pool) > 0: + self.rw_iter = itertools.cycle(rw_pool) else: - self.RW_iter = None + self.rw_iter = None - if len(RO_pool) > 0: - self.RO_iter = itertools.cycle(RO_pool) + if len(ro_pool) > 0: + self.ro_iter = itertools.cycle(ro_pool) else: - self.RO_iter = None + self.ro_iter = None self.rebuild_needed = False @@ -287,32 +287,32 @@ def getnext(self, mode): self.build() if mode == Mode.ANY: - if self.ANY_iter is not None: - return next(self.ANY_iter) + if self.any_iter is not None: + return next(self.any_iter) else: raise PoolTolopogyError("Can't find healthy instance in pool") elif mode == Mode.RW: - if self.RW_iter is not None: - return next(self.RW_iter) + if self.rw_iter is not None: + return next(self.rw_iter) else: raise PoolTolopogyError("Can't find healthy rw instance in pool") elif mode == Mode.RO: - if self.RO_iter is not None: - return next(self.RO_iter) + if self.ro_iter is not None: + return next(self.ro_iter) else: raise PoolTolopogyError("Can't find healthy ro instance in pool") elif mode == Mode.PREFER_RO: - if self.RO_iter is not None: - return next(self.RO_iter) - elif self.RW_iter is not None: - return next(self.RW_iter) + if self.ro_iter is not None: + return next(self.ro_iter) + elif self.rw_iter is not None: + return next(self.rw_iter) else: raise PoolTolopogyError("Can't find healthy instance in pool") elif mode == Mode.PREFER_RW: - if self.RW_iter is not None: - return next(self.RW_iter) - elif self.RO_iter is not None: - return next(self.RO_iter) + if self.rw_iter is not None: + return next(self.rw_iter) + elif self.ro_iter is not None: + return next(self.ro_iter) else: raise PoolTolopogyError("Can't find healthy instance in pool") @@ -545,7 +545,7 @@ def _get_new_state(self, unit): if conn.is_closed(): try: conn.connect() - except NetworkError as e: + except NetworkError as exc: msg = "Failed to connect to {0}:{1}".format( unit.addr['host'], unit.addr['port']) warn(msg, ClusterConnectWarning) @@ -553,15 +553,15 @@ def _get_new_state(self, unit): try: resp = conn.call('box.info') - except NetworkError as e: + except NetworkError as exc: msg = "Failed to get box.info for {0}:{1}, reason: {2}".format( - unit.addr['host'], unit.addr['port'], repr(e)) + unit.addr['host'], unit.addr['port'], repr(exc)) warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) try: - ro = resp.data[0]['ro'] - except (IndexError, KeyError) as e: + read_only = resp.data[0]['ro'] + except (IndexError, KeyError) as exc: msg = "Incorrect box.info response from {0}:{1}".format( unit.addr['host'], unit.addr['port']) warn(msg, PoolTolopogyWarning) @@ -575,13 +575,13 @@ def _get_new_state(self, unit): unit.addr['host'], unit.addr['port']) warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) - except (IndexError, KeyError) as e: + except (IndexError, KeyError) as exc: msg = "Incorrect box.info response from {0}:{1}".format( unit.addr['host'], unit.addr['port']) warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) - return InstanceState(Status.HEALTHY, ro) + return InstanceState(Status.HEALTHY, read_only) def _refresh_state(self, key): """ @@ -644,8 +644,8 @@ def _request_process_loop(self, key, unit, last_refresh): method = getattr(Connection, task.method_name) try: resp = method(unit.conn, *task.args, **task.kwargs) - except Exception as e: - unit.output_queue.put(e) + except Exception as exc: + unit.output_queue.put(exc) else: unit.output_queue.put(resp) diff --git a/tarantool/crud.py b/tarantool/crud.py index 044271a9..8173847e 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -64,9 +64,9 @@ def call_crud(conn, *args): try: crud_resp = conn.call(*args) - except DatabaseError as e: - if e.code == ER_NO_SUCH_PROC or e.code == ER_ACCESS_DENIED: + except DatabaseError as exc: + if exc.code == ER_NO_SUCH_PROC or exc.code == ER_ACCESS_DENIED: exc_msg = ". Ensure that you're calling crud.router and user has sufficient grants" - raise DatabaseError(e.code, e.message + exc_msg, extra_info=e.extra_info) from e + raise DatabaseError(exc.code, exc.message + exc_msg, extra_info=exc.extra_info) from exc return crud_resp diff --git a/tarantool/error.py b/tarantool/error.py index 99fc7c7b..7b4181cd 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -8,9 +8,9 @@ import socket try: import ssl - is_ssl_supported = True + IS_SSL_SUPPORTED = True except ImportError: - is_ssl_supported = False + IS_SSL_SUPPORTED = False import sys import warnings @@ -291,7 +291,7 @@ def __init__(self, orig_exception=None, *args): if hasattr(orig_exception, 'errno'): self.errno = orig_exception.errno if orig_exception: - if is_ssl_supported and isinstance(orig_exception, ssl.SSLError): + if IS_SSL_SUPPORTED and isinstance(orig_exception, ssl.SSLError): super(SslError, self).__init__(orig_exception, *args) else: super(SslError, self).__init__(orig_exception, *args) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 2cb44ee3..38da6dba 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -88,17 +88,17 @@ def parse_error(uri, msg): except ValueError: return parse_error(uri, 'port should be a number') - for k, v in default_addr_opts.items(): - result[k] = v + for key, val in default_addr_opts.items(): + result[key] = val if opts_str != "": for opt_str in opts_str.split('&'): opt = opt_str.split('=') if len(opt) != 2: continue - for k in default_addr_opts: - if k == opt[0]: - result[k] = opt[1] + for key in default_addr_opts: + if key == opt[0]: + result[key] = opt[1] return result, None @@ -128,12 +128,12 @@ def format_error(address, err): return format_error(address, 'port is not set or None') result = {} - for k, v in address.items(): - result[k] = v + for key, val in address.items(): + result[key] = val # Set default values. - for k, v in default_addr_opts.items(): - if k not in result: - result[k] = v + for key, val in default_addr_opts.items(): + if key not in result: + result[key] = val if isinstance(result['port'], int): # Looks like an inet address. @@ -527,8 +527,8 @@ def _opt_reconnect(self): super(MeshConnection, self)._opt_reconnect() last_error = None break - except NetworkError as e: - last_error = e + except NetworkError as exc: + last_error = exc addr = self.strategy.getnext() update_connection(self, addr) @@ -558,8 +558,8 @@ def _opt_refresh_instances(self): self.call_16) try: resp = self._send_request_wo_reconnect(request) - except DatabaseError as e: - msg = 'got "%s" error, skipped address updates' % str(e) + except DatabaseError as exc: + msg = 'got "%s" error, skipped address updates' % str(exc) warn(msg, ClusterDiscoveryWarning) return diff --git a/tarantool/msgpack_ext/datetime.py b/tarantool/msgpack_ext/datetime.py index 64422f5d..a187940e 100644 --- a/tarantool/msgpack_ext/datetime.py +++ b/tarantool/msgpack_ext/datetime.py @@ -95,9 +95,9 @@ def encode(obj, _): nsec = obj.nsec tzoffset = obj.tzoffset - tz = obj.tz - if tz != '': - tzindex = tt_timezones.timezoneToIndex[tz] + timezone = obj.tz + if timezone != '': + tzindex = tt_timezones.timezoneToIndex[timezone] else: tzindex = 0 diff --git a/tarantool/msgpack_ext/interval.py b/tarantool/msgpack_ext/interval.py index cd7ab16d..9fd26682 100644 --- a/tarantool/msgpack_ext/interval.py +++ b/tarantool/msgpack_ext/interval.py @@ -127,8 +127,8 @@ def decode(data, unpacker): if field_name == 'adjust': try: value = Adjust(value) - except ValueError as e: - raise MsgpackError(e) + except ValueError as exc: + raise MsgpackError(exc) kwargs[id_map[field_id]] = value diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index d2e4b903..ac191dbb 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -517,8 +517,8 @@ def __deepcopy__(self, memo): cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result - for k, v in self.__dict__.items(): - setattr(result, k, deepcopy(v, memo)) + for key, val in self.__dict__.items(): + setattr(result, key, deepcopy(val, memo)) return result @property diff --git a/tarantool/schema.py b/tarantool/schema.py index ad96429f..fb000333 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -28,11 +28,11 @@ class RecursionError(Error): """ -def to_unicode(s): +def to_unicode(string): """ Decode :obj:`bytes` to unicode :obj:`str`. - :param s: Value to convert. + :param string: Value to convert. :return: Decoded unicode :obj:`str`, if value is :obj:`bytes`. Otherwise, it returns the original value. @@ -40,17 +40,17 @@ def to_unicode(s): :meta private: """ - if isinstance(s, bytes): - return s.decode(encoding='utf-8') - return s + if isinstance(string, bytes): + return string.decode(encoding='utf-8') + return string -def to_unicode_recursive(x, max_depth): +def to_unicode_recursive(value, max_depth): """ Recursively decode :obj:`bytes` to unicode :obj:`str` over :obj:`dict`, :obj:`list` and :obj:`tuple`. - :param x: Value to convert. + :param value: Value to convert. :param max_depth: Maximum depth recursion. :type max_depth: :obj:`int` @@ -66,24 +66,24 @@ def to_unicode_recursive(x, max_depth): if max_depth <= 0: raise RecursionError('Max recursion depth is reached') - if isinstance(x, dict): + if isinstance(value, dict): res = dict() - for key, val in x.items(): + for key, val in value.items(): key = to_unicode_recursive(key, max_depth - 1) val = to_unicode_recursive(val, max_depth - 1) res[key] = val return res - if isinstance(x, list) or isinstance(x, tuple): + if isinstance(value, list) or isinstance(value, tuple): res = [] - for val in x: - val = to_unicode_recursive(val, max_depth - 1) - res.append(val) - if isinstance(x, tuple): + for item in value: + item = to_unicode_recursive(item, max_depth - 1) + res.append(item) + if isinstance(value, tuple): return tuple(res) return res - return to_unicode(x) + return to_unicode(value) class SchemaIndex(object): @@ -109,8 +109,8 @@ def __init__(self, index_row, space): self.parts = [] try: parts_raw = to_unicode_recursive(index_row[5], MAX_RECURSION_DEPTH) - except RecursionError as e: - errmsg = 'Unexpected index parts structure: ' + str(e) + except RecursionError as exc: + errmsg = 'Unexpected index parts structure: ' + str(exc) raise SchemaError(errmsg) if isinstance(parts_raw, (list, tuple)): for val in parts_raw: @@ -166,8 +166,8 @@ def __init__(self, space_row, schema): self.format = dict() try: format_raw = to_unicode_recursive(space_row[6], MAX_RECURSION_DEPTH) - except RecursionError as e: - errmsg = 'Unexpected space format structure: ' + str(e) + except RecursionError as exc: + errmsg = 'Unexpected space format structure: ' + str(exc) raise SchemaError(errmsg) for part_id, part in enumerate(format_raw): part['id'] = part_id @@ -279,10 +279,10 @@ def fetch_space_from(self, space): # Try to fetch from '_vspace' space_row = self.con.select(const.SPACE_VSPACE, space, index=_index) - except DatabaseError as e: + except DatabaseError as exc: # if space can't be found, then user is using old version of # tarantool, try again with '_space' - if e.args[0] != 36: + if exc.args[0] != 36: raise if space_row is None: # Try to fetch from '_space' @@ -416,10 +416,10 @@ def fetch_index_from(self, space, index): # Try to fetch from '_vindex' index_row = self.con.select(const.SPACE_VINDEX, _key_tuple, index=_index) - except DatabaseError as e: + except DatabaseError as exc: # if space can't be found, then user is using old version of # tarantool, try again with '_index' - if e.args[0] != 36: + if exc.args[0] != 36: raise if index_row is None: # Try to fetch from '_index' @@ -451,9 +451,9 @@ def get_field(self, space, field): try: return _space.format[field] except: - tp = 'name' if isinstance(field, str) else 'id' + kind = 'name' if isinstance(field, str) else 'id' errmsg = "There's no field with {2} '{0}' in space '{1}'".format( - field, _space.name, tp + field, _space.name, kind ) raise SchemaError(errmsg) diff --git a/tarantool/utils.py b/tarantool/utils.py index e5238707..b0f0614d 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -115,6 +115,6 @@ class Greeting: raise Exception("x") # Unsupported greeting result.salt = base64_decode(greeting_buf[64:])[:20] return result - except Exception as e: - print('exx', e) + except Exception as exc: + print('exx', exc) raise ValueError("Invalid greeting: " + str(greeting_buf)) diff --git a/test/setup_command.py b/test/setup_command.py index da0cb6c4..fe250743 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -8,7 +8,7 @@ from glob import glob -class test(setuptools.Command): +class Test(setuptools.Command): user_options = [] description = 'Run tests' diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index c37fba0d..7cb222e6 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -22,7 +22,7 @@ def fetch_tarantool_version(self): except: self.__class__.tnt_version = srv.admin.tnt_version -def skip_or_run_test_tarantool_impl(self, REQUIRED_TNT_VERSION, msg): +def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): """Helper to skip or run tests depending on the Tarantool version. @@ -31,13 +31,13 @@ def skip_or_run_test_tarantool_impl(self, REQUIRED_TNT_VERSION, msg): """ fetch_tarantool_version(self) - support_version = pkg_resources.parse_version(REQUIRED_TNT_VERSION) + support_version = pkg_resources.parse_version(required_tt_version) if self.tnt_version < support_version: self.skipTest('Tarantool %s %s' % (self.tnt_version, msg)) -def skip_or_run_test_tarantool(func, REQUIRED_TNT_VERSION, msg): +def skip_or_run_test_tarantool(func, required_tt_version, msg): """Decorator to skip or run tests depending on the tarantool version. @@ -50,14 +50,14 @@ def wrapper(self, *args, **kwargs): if func.__name__ == 'setUp': func(self, *args, **kwargs) - skip_or_run_test_tarantool_impl(self, REQUIRED_TNT_VERSION, msg) + skip_or_run_test_tarantool_impl(self, required_tt_version, msg) if func.__name__ != 'setUp': func(self, *args, **kwargs) return wrapper -def skip_or_run_test_tarantool_call(self, REQUIRED_TNT_VERSION, msg): +def skip_or_run_test_tarantool_call(self, required_tt_version, msg): """Function to skip or run tests depending on the tarantool version. Useful in cases when in is inconvenient to work with decorators. @@ -66,10 +66,10 @@ def skip_or_run_test_tarantool_call(self, REQUIRED_TNT_VERSION, msg): the whole test suite. """ - skip_or_run_test_tarantool_impl(self, REQUIRED_TNT_VERSION, msg) + skip_or_run_test_tarantool_impl(self, required_tt_version, msg) -def skip_or_run_test_pcall_require(func, REQUIRED_TNT_MODULE, msg): +def skip_or_run_test_pcall_require(func, required_tt_module, msg): """Decorator to skip or run tests depending on tarantool module requre success or fail. @@ -92,7 +92,7 @@ def wrapper(self, *args, **kwargs): assert srv is not None - resp = srv.admin("pcall(require, '%s')" % REQUIRED_TNT_MODULE) + resp = srv.admin("pcall(require, '%s')" % required_tt_module) if not resp[0]: self.skipTest('Tarantool %s' % (msg, )) @@ -102,7 +102,7 @@ def wrapper(self, *args, **kwargs): return wrapper -def skip_or_run_test_python(func, REQUIRED_PYTHON_VERSION, msg): +def skip_or_run_test_python(func, required_python_version, msg): """Decorator to skip or run tests depending on the Python version. Also, it can be used with the 'setUp' method for skipping @@ -117,7 +117,7 @@ def wrapper(self, *args, **kwargs): ver = sys.version_info python_version_str = '%d.%d' % (ver.major, ver.minor) python_version = pkg_resources.parse_version(python_version_str) - support_version = pkg_resources.parse_version(REQUIRED_PYTHON_VERSION) + support_version = pkg_resources.parse_version(required_python_version) if python_version < support_version: self.skipTest('Python %s connector %s' % (python_version, msg)) @@ -162,7 +162,7 @@ def skip_or_run_decimal_test(func): return skip_or_run_test_pcall_require(func, 'decimal', 'does not support decimal type') -def skip_or_run_UUID_test(func): +def skip_or_run_uuid_test(func): """Decorator to skip or run UUID-related tests depending on the tarantool version. diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index 33a6d61f..77bb2528 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -32,7 +32,7 @@ def __enter__(self): self.connect() return self - def __exit__(self, type, value, tb): + def __exit__(self, type, value, traceback): self.disconnect() def __call__(self, command): diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index ceecb5a7..ffff1320 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -235,8 +235,8 @@ def wait_until_started(self): continue else: raise Exception("Strange output for `box.info.status`: %s" % (ans)) - except socket.error as e: - if e.errno == errno.ECONNREFUSED: + except socket.error as exc: + if exc.errno == errno.ECONNREFUSED: time.sleep(0.1) continue raise diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 4132d35e..3cd46aba 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -10,7 +10,7 @@ from .lib.skip import skip_or_run_decimal_test, skip_or_run_varbinary_test from .lib.tarantool_server import TarantoolServer -class TestSuite_Connection(unittest.TestCase): +class TestSuiteConnection(unittest.TestCase): @classmethod def setUpClass(self): print(' CONNECTION '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 90281a65..2699fe7c 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -18,7 +18,7 @@ def create_server(): @unittest.skipIf(sys.platform.startswith("win"), "Crud tests on windows platform are not supported: " + "complexity of the vshard replicaset configuration") -class TestSuite_Crud(unittest.TestCase): +class TestSuiteCrud(unittest.TestCase): @classmethod def setUpClass(self): @@ -699,21 +699,21 @@ def _exception_operation_with_crud(self, testing_function, case, mode=None): _ = testing_function( *case['error']['input']['args'], ) - except DatabaseError as e: + except DatabaseError as exc: for regexp_case in case['error']['output']['str']: - if hasattr(e, 'extra_info_error'): + if hasattr(exc, 'extra_info_error'): # Case for non-batch operations. - self.assertNotEqual(re.search(regexp_case, e.extra_info_error.str), None) - if hasattr(e, 'errors_list'): + self.assertNotEqual(re.search(regexp_case, exc.extra_info_error.str), None) + if hasattr(exc, 'errors_list'): # Case for *_many() operations. err_sum = str() - for err in e.errors_list: + for err in exc.errors_list: err_sum = err_sum + err.str self.assertNotEqual(re.search(regexp_case, err_sum), None) - if hasattr(e, 'success_list'): + if hasattr(exc, 'success_list'): # Case for *_many() operations. if 'res_rows' in case['error']['output']: - self.assertEqual(e.success_list.rows, case['error']['output']['res_rows']) + self.assertEqual(exc.success_list.rows, case['error']['output']['res_rows']) def test_crud_module_via_connection(self): for case_name in self.crud_test_cases.keys(): diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index e705ec74..313d49cc 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -13,7 +13,7 @@ from .lib.skip import skip_or_run_datetime_test from tarantool.error import MsgpackError, MsgpackWarning -class TestSuite_Datetime(unittest.TestCase): +class TestSuiteDatetime(unittest.TestCase): @classmethod def setUpClass(self): print(' DATETIME EXT TYPE '.center(70, '='), file=sys.stderr) @@ -65,39 +65,39 @@ def setUp(self): self.adm("box.space['test']:truncate()") - def test_Datetime_class_API(self): - dt = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, - nsec=308543321, tzoffset=180) + def test_datetime_class_api(self): + datetime = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tzoffset=180) - self.assertEqual(dt.year, 2022) - self.assertEqual(dt.month, 8) - self.assertEqual(dt.day, 31) - self.assertEqual(dt.hour, 18) - self.assertEqual(dt.minute, 7) - self.assertEqual(dt.sec, 54) - self.assertEqual(dt.nsec, 308543321) + self.assertEqual(datetime.year, 2022) + self.assertEqual(datetime.month, 8) + self.assertEqual(datetime.day, 31) + self.assertEqual(datetime.hour, 18) + self.assertEqual(datetime.minute, 7) + self.assertEqual(datetime.sec, 54) + self.assertEqual(datetime.nsec, 308543321) # Both Tarantool and pandas prone to precision loss for timestamp() floats - self.assertEqual(dt.timestamp, 1661958474.308543) - self.assertEqual(dt.tzoffset, 180) - self.assertEqual(dt.tz, '') - self.assertEqual(dt.value, 1661958474308543321) - - def test_Datetime_class_API_wth_tz(self): - dt = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, - nsec=308543321, tzoffset=123, tz='Europe/Moscow') - - self.assertEqual(dt.year, 2022) - self.assertEqual(dt.month, 8) - self.assertEqual(dt.day, 31) - self.assertEqual(dt.hour, 18) - self.assertEqual(dt.minute, 7) - self.assertEqual(dt.sec, 54) - self.assertEqual(dt.nsec, 308543321) + self.assertEqual(datetime.timestamp, 1661958474.308543) + self.assertEqual(datetime.tzoffset, 180) + self.assertEqual(datetime.tz, '') + self.assertEqual(datetime.value, 1661958474308543321) + + def test_datetime_class_api_wth_tz(self): + datetime = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tzoffset=123, tz='Europe/Moscow') + + self.assertEqual(datetime.year, 2022) + self.assertEqual(datetime.month, 8) + self.assertEqual(datetime.day, 31) + self.assertEqual(datetime.hour, 18) + self.assertEqual(datetime.minute, 7) + self.assertEqual(datetime.sec, 54) + self.assertEqual(datetime.nsec, 308543321) # Both Tarantool and pandas prone to precision loss for timestamp() floats - self.assertEqual(dt.timestamp, 1661958474.308543) - self.assertEqual(dt.tzoffset, 180) - self.assertEqual(dt.tz, 'Europe/Moscow') - self.assertEqual(dt.value, 1661958474308543321) + self.assertEqual(datetime.timestamp, 1661958474.308543) + self.assertEqual(datetime.tzoffset, 180) + self.assertEqual(datetime.tz, 'Europe/Moscow') + self.assertEqual(datetime.value, 1661958474308543321) datetime_class_invalid_init_cases = { @@ -139,7 +139,7 @@ def test_Datetime_class_API_wth_tz(self): }, } - def test_Datetime_class_invalid_init(self): + def test_datetime_class_invalid_init(self): for name in self.datetime_class_invalid_init_cases.keys(): with self.subTest(msg=name): case = self.datetime_class_invalid_init_cases[name] diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 5e35bb61..e2efe571 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -9,7 +9,7 @@ from .lib.skip import skip_or_run_sql_test -class TestSuite_DBAPI(dbapi20.DatabaseAPI20Test): +class TestSuiteDBAPI(dbapi20.DatabaseAPI20Test): table_prefix = 'dbapi20test_' # If you need to specify a prefix for tables ddl0 = 'create table %s (id INTEGER PRIMARY KEY AUTOINCREMENT, ' \ diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index e64e6b54..90052edc 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -12,7 +12,7 @@ from .lib.skip import skip_or_run_decimal_test from tarantool.error import MsgpackError, MsgpackWarning -class TestSuite_Decimal(unittest.TestCase): +class TestSuiteDecimal(unittest.TestCase): @classmethod def setUpClass(self): print(' DECIMAL EXT TYPE '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 2dbff1b5..4b3461bd 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -6,7 +6,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_extra_info_test -class TestSuite_Request(unittest.TestCase): +class TestSuiteRequest(unittest.TestCase): @classmethod def setUpClass(self): print(' DML '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 45bc6053..649ff080 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -7,7 +7,7 @@ from .lib.skip import skip_or_run_varbinary_test, skip_or_run_error_extra_info_test from .lib.tarantool_server import TarantoolServer -class TestSuite_Encoding(unittest.TestCase): +class TestSuiteEncoding(unittest.TestCase): @classmethod def setUpClass(self): print(' ENCODING '.center(70, '='), file=sys.stderr) @@ -66,8 +66,8 @@ def setUpClass(self): def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) - except Exception as e: - self.fail('Function raised Exception: %s' % repr(e)) + except Exception as exc: + self.fail('Function raised Exception: %s' % repr(exc)) def setUp(self): # prevent a remote tarantool from clean our session diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index abf8d93d..4831830f 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -12,7 +12,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_ext_type_test -class TestSuite_ErrorExt(unittest.TestCase): +class TestSuiteErrorExt(unittest.TestCase): @classmethod def setUpClass(self): print(' ERROR EXT TYPE '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index f90b2fc3..67e82288 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -6,7 +6,7 @@ from .lib.skip import skip_or_run_sql_test -class TestSuite_Execute(unittest.TestCase): +class TestSuiteExecute(unittest.TestCase): ddl = 'create table %s (id INTEGER PRIMARY KEY AUTOINCREMENT, ' \ 'name varchar(20))' diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 343c95fb..659719e1 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -14,7 +14,7 @@ from .lib.skip import skip_or_run_datetime_test from tarantool.error import MsgpackError -class TestSuite_Interval(unittest.TestCase): +class TestSuiteInterval(unittest.TestCase): @classmethod def setUpClass(self): print(' INTERVAL EXT TYPE '.center(70, '='), file=sys.stderr) @@ -57,7 +57,7 @@ def setUp(self): self.adm("box.space['test']:truncate()") - def test_Interval_positional_init(self): + def test_interval_positional_init(self): self.assertRaisesRegex( TypeError, re.escape('__init__() takes 1 positional argument but 2 were given'), lambda: tarantool.Interval(1)) diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index ae621bf5..2fd87bd7 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -27,7 +27,7 @@ def create_server(_id): @unittest.skipIf(sys.platform.startswith("win"), 'Mesh tests on windows platform are not supported') -class TestSuite_Mesh(unittest.TestCase): +class TestSuiteMesh(unittest.TestCase): def define_cluster_function(self, func_name, servers): addresses = [(srv.host, srv.args['primary']) for srv in servers] addresses_lua = ",".join("'%s:%d'" % address for address in addresses) @@ -158,11 +158,11 @@ def test_02_discovery_bad_address(self): # Verify that a cluster discovery (that is triggered # by ping) give one or two warnings. - with warnings.catch_warnings(record=True) as ws: + with warnings.catch_warnings(record=True) as warns: con.ping() - self.assertTrue(len(ws) in (1, 2)) - for w in ws: - self.assertIs(w.category, ClusterDiscoveryWarning) + self.assertTrue(len(warns) in (1, 2)) + for warn in warns: + self.assertIs(warn.category, ClusterDiscoveryWarning) # Verify that incorrect or empty result was discarded. self.assertEqual(len(con.strategy.addrs), 1) @@ -182,10 +182,10 @@ def test_03_discovery_bad_good_addresses(self): # Verify that a cluster discovery (that is triggered # by ping) give one warning. - with warnings.catch_warnings(record=True) as ws: + with warnings.catch_warnings(record=True) as warns: con.ping() - self.assertEqual(len(ws), 1) - self.assertIs(ws[0].category, ClusterDiscoveryWarning) + self.assertEqual(len(warns), 1) + self.assertIs(warns[0].category, ClusterDiscoveryWarning) # Verify that only second address was accepted. self.assertEqual(len(con.strategy.addrs), 1) diff --git a/test/suites/test_package.py b/test/suites/test_package.py index a136c958..7b40a150 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -18,7 +18,7 @@ def is_test_pure_install(): return False -class TestSuite_Package(unittest.TestCase): +class TestSuitePackage(unittest.TestCase): @classmethod def setUpClass(self): print(' PACKAGE '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index cfc7c562..bdb7284a 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -45,7 +45,7 @@ def create_server(_id): @unittest.skipIf(sys.platform.startswith("win"), 'Pool tests on windows platform are not supported') -class TestSuite_Pool(unittest.TestCase): +class TestSuitePool(unittest.TestCase): def set_ro(self, srv, read_only): if read_only: req = r'box.cfg{read_only = true}' @@ -64,9 +64,9 @@ def retry(self, func, count=5, timeout=0.5): for i in range(count): try: func() - except Exception as e: + except Exception as exc: if i + 1 == count: - raise e + raise exc time.sleep(timeout) @@ -108,8 +108,8 @@ def test_00_basic(self): def test_01_roundrobin(self): self.set_cluster_ro([False, False, True, False, True]) - RW_ports = set([str(self.addrs[0]['port']), str(self.addrs[1]['port']), str(self.addrs[3]['port'])]) - RO_ports = set([str(self.addrs[2]['port']), str(self.addrs[4]['port'])]) + rw_ports = set([str(self.addrs[0]['port']), str(self.addrs[1]['port']), str(self.addrs[3]['port'])]) + ro_ports = set([str(self.addrs[2]['port']), str(self.addrs[4]['port'])]) all_ports = set() for addr in self.addrs: all_ports.add(str(addr['port'])) @@ -126,79 +126,79 @@ def get_port(self, mode): return resp.data[0] # Expect ANY iterate through all instances. - ANY_ports_result = set() + any_ports_result = set() for i in range(len(self.servers)): - ANY_ports_result.add(get_port(self, tarantool.Mode.ANY)) + any_ports_result.add(get_port(self, tarantool.Mode.ANY)) - self.assertSetEqual(ANY_ports_result, all_ports) + self.assertSetEqual(any_ports_result, all_ports) # Expect RW iterate through all RW instances. - RW_ports_result = set() + rw_ports_result = set() for i in range(len(self.servers)): - RW_ports_result.add(get_port(self, tarantool.Mode.RW)) + rw_ports_result.add(get_port(self, tarantool.Mode.RW)) - self.assertSetEqual(RW_ports_result, RW_ports) + self.assertSetEqual(rw_ports_result, rw_ports) # Expect RO iterate through all RO instances. - RO_ports_result = set() + ro_ports_result = set() for i in range(len(self.servers)): - RO_ports_result.add(get_port(self, tarantool.Mode.RO)) + ro_ports_result.add(get_port(self, tarantool.Mode.RO)) - self.assertSetEqual(RO_ports_result, RO_ports) + self.assertSetEqual(ro_ports_result, ro_ports) # Expect PREFER_RW iterate through all RW instances if there is at least one. - PREFER_RW_ports_result = set() + prefer_rw_ports_result = set() for i in range(len(self.servers)): - PREFER_RW_ports_result.add(get_port(self, tarantool.Mode.PREFER_RW)) + prefer_rw_ports_result.add(get_port(self, tarantool.Mode.PREFER_RW)) - self.assertSetEqual(PREFER_RW_ports_result, RW_ports) + self.assertSetEqual(prefer_rw_ports_result, rw_ports) # Expect PREFER_RO iterate through all RO instances if there is at least one. - PREFER_RO_ports_result = set() + prefer_ro_ports_result = set() for i in range(len(self.servers)): - PREFER_RO_ports_result.add(get_port(self, tarantool.Mode.PREFER_RO)) + prefer_ro_ports_result.add(get_port(self, tarantool.Mode.PREFER_RO)) - self.assertSetEqual(PREFER_RO_ports_result, RO_ports) + self.assertSetEqual(prefer_ro_ports_result, ro_ports) # Setup cluster with no RW. self.set_cluster_ro([True, True, True, True, True]) # Expect RW to fail if there are no RW. - def expect_RW_to_fail_if_there_are_no_RW(): + def expect_rw_to_fail_if_there_are_no_rw(): with self.assertRaises(PoolTolopogyError): self.pool.eval('return box.cfg.listen', mode=tarantool.Mode.RW) - self.retry(func=expect_RW_to_fail_if_there_are_no_RW) + self.retry(func=expect_rw_to_fail_if_there_are_no_rw) # Expect PREFER_RW iterate through all instances if there are no RW. - def expect_PREFER_RW_iterate_through_all_instances_if_there_are_no_RW(): - PREFER_RW_ports_result_all_ro = set() + def expect_prefer_rw_iterate_through_all_instances_if_there_are_no_rw(): + prefer_rw_ports_result_all_ro = set() for i in range(len(self.servers)): - PREFER_RW_ports_result_all_ro.add(get_port(self, tarantool.Mode.PREFER_RW)) + prefer_rw_ports_result_all_ro.add(get_port(self, tarantool.Mode.PREFER_RW)) - self.assertSetEqual(PREFER_RW_ports_result_all_ro, all_ports) + self.assertSetEqual(prefer_rw_ports_result_all_ro, all_ports) - self.retry(func=expect_PREFER_RW_iterate_through_all_instances_if_there_are_no_RW) + self.retry(func=expect_prefer_rw_iterate_through_all_instances_if_there_are_no_rw) # Setup cluster with no RO. self.set_cluster_ro([False, False, False, False, False]) # Expect RO to fail if there are no RO. - def expect_RO_to_fail_if_there_are_no_RO(): + def expect_ro_to_fail_if_there_are_no_ro(): with self.assertRaises(PoolTolopogyError): self.pool.eval('return box.cfg.listen', mode=tarantool.Mode.RO) - self.retry(func=expect_RO_to_fail_if_there_are_no_RO) + self.retry(func=expect_ro_to_fail_if_there_are_no_ro) # Expect PREFER_RO iterate through all instances if there are no RO. - def expect_PREFER_RO_iterate_through_all_instances_if_there_are_no_RO(): - PREFER_RO_ports_result_all_rw = set() + def expect_prefer_ro_iterate_through_all_instances_if_there_are_no_ro(): + prefer_ro_ports_result_all_rw = set() for i in range(len(self.servers)): - PREFER_RO_ports_result_all_rw.add(get_port(self, tarantool.Mode.PREFER_RO)) + prefer_ro_ports_result_all_rw.add(get_port(self, tarantool.Mode.PREFER_RO)) - self.assertSetEqual(PREFER_RO_ports_result_all_rw, all_ports) + self.assertSetEqual(prefer_ro_ports_result_all_rw, all_ports) - self.retry(func=expect_PREFER_RO_iterate_through_all_instances_if_there_are_no_RO) + self.retry(func=expect_prefer_ro_iterate_through_all_instances_if_there_are_no_ro) def test_02_exception_raise(self): self.set_cluster_ro([False, False, True, False, True]) @@ -509,12 +509,12 @@ def test_13_failover(self): self.servers[0].stop() self.set_ro(self.servers[1], False) - def expect_RW_request_execute_on_new_master(): + def expect_rw_request_execute_on_new_master(): self.assertSequenceEqual( self.pool.eval('return box.cfg.listen', mode=tarantool.Mode.RW), [ str(self.addrs[1]['port']) ]) - self.retry(func=expect_RW_request_execute_on_new_master) + self.retry(func=expect_rw_request_execute_on_new_master) def test_14_cluster_with_instances_dead_in_runtime_is_ok(self): warnings.simplefilter('ignore', category=ClusterConnectWarning) @@ -544,10 +544,10 @@ def test_15_cluster_with_dead_instances_on_start_is_ok(self): self.servers[0].start() - def ping_RW(): + def ping_rw(): self.pool.ping(mode=tarantool.Mode.RW) - self.retry(func=ping_RW) + self.retry(func=ping_rw) def test_16_is_closed(self): self.set_cluster_ro([False, False, True, False, True]) diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 6a548e4f..4b6b2de2 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -15,7 +15,7 @@ IPROTO_FEATURE_WATCHERS, ) -class TestSuite_Protocol(unittest.TestCase): +class TestSuiteProtocol(unittest.TestCase): @classmethod def setUpClass(self): print(' PROTOCOL '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 91d0f82e..21f822e5 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -74,7 +74,7 @@ def push_callback(data, on_push_ctx=[]): on_push_ctx.append(data) -class TestSuite_Push(unittest.TestCase): +class TestSuitePush(unittest.TestCase): @classmethod def setUpClass(self): diff --git a/test/suites/test_reconnect.py b/test/suites/test_reconnect.py index fc41cb57..61616e46 100644 --- a/test/suites/test_reconnect.py +++ b/test/suites/test_reconnect.py @@ -5,7 +5,7 @@ from .lib.tarantool_server import TarantoolServer -class TestSuite_Reconnect(unittest.TestCase): +class TestSuiteReconnect(unittest.TestCase): @classmethod def setUpClass(self): print(' RECONNECT '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 85deace1..1607b621 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -34,7 +34,7 @@ def call_count(self): return self._call_count -class TestSuite_Schema_Abstract(unittest.TestCase): +class TestSuiteSchemaAbstract(unittest.TestCase): # Define 'encoding' field in a concrete class. @classmethod @@ -393,8 +393,8 @@ def test_06_index_cached(self): (self.unicode_space_id, self.unicode_index_name_literal), (self.unicode_space_id, self.unicode_index_id), ) - for s, i in cases: - index = self.sch.get_index(s, i) + for space, index_id in cases: + index = self.sch.get_index(space, index_id) self.verify_unicode_index(index) # Verify that no schema fetches occurs. @@ -483,9 +483,9 @@ def _run_test_schema_fetch_disable(self, con, mode=None): 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') + except NotSupportedError as exc: + self.assertEqual(exc.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(): @@ -608,9 +608,9 @@ def tearDownClass(self): self.srv.clean() -class TestSuite_Schema_UnicodeConnection(TestSuite_Schema_Abstract): +class TestSuiteSchemaUnicodeConnection(TestSuiteSchemaAbstract): encoding = 'utf-8' -class TestSuite_Schema_BinaryConnection(TestSuite_Schema_Abstract): +class TestSuiteSchemaBinaryConnection(TestSuiteSchemaAbstract): encoding = None diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 36e172de..23c89cc7 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -70,7 +70,7 @@ def __init__(self, self.client_auth_type = client_auth_type @unittest.skipIf(not is_test_ssl(), "TEST_TNT_SSL is not set.") -class TestSuite_Ssl(unittest.TestCase): +class TestSuiteSsl(unittest.TestCase): @classmethod def setUpClass(self): print(' SSL '.center(70, '='), file=sys.stderr) diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index f81f1117..f00c544e 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -9,10 +9,10 @@ from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer -from .lib.skip import skip_or_run_UUID_test +from .lib.skip import skip_or_run_uuid_test from tarantool.error import MsgpackError, MsgpackWarning -class TestSuite_UUID(unittest.TestCase): +class TestSuiteUUID(unittest.TestCase): @classmethod def setUpClass(self): print(' UUID EXT TYPE '.center(70, '='), file=sys.stderr) @@ -90,7 +90,7 @@ def test_msgpack_decode(self): self.assertEqual(unpacker_ext_hook(2, case['msgpack']), case['python']) - @skip_or_run_UUID_test + @skip_or_run_uuid_test def test_tarantool_decode(self): for name in self.cases.keys(): with self.subTest(msg=name): @@ -109,7 +109,7 @@ def test_msgpack_encode(self): self.assertEqual(packer_default(case['python']), msgpack.ExtType(code=2, data=case['msgpack'])) - @skip_or_run_UUID_test + @skip_or_run_uuid_test def test_tarantool_encode(self): for name in self.cases.keys(): with self.subTest(msg=name): @@ -133,7 +133,7 @@ def test_tarantool_encode(self): self.assertSequenceEqual(self.con.eval(lua_eval), [True]) - @skip_or_run_UUID_test + @skip_or_run_uuid_test def test_primary_key(self): data = [uuid.UUID('ae28d4f6-076c-49dd-8227-7f9fae9592d0'), 'content'] From 455f7062b8d4aede10afb415e721c918b0556368 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:39:13 +0300 Subject: [PATCH 021/190] pylint: allow some short variables `t` sounds like a reasonable variable names in tests, `ok` sounds like a reasonable variable names in general. This patch disables C0103 invalid-name check for them. Part of #270 --- .pylintrc | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..c0043d08 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,4 @@ +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_,ok,t From 59320cf300a162b6a9802ce8159f09476c5378c2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:42:09 +0300 Subject: [PATCH 022/190] pylint: treat tz as a good name `tz` is a part of the Datetime public API and we don't plan to change it now. Part of #270 --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index c0043d08..1f506706 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ [BASIC] # Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_,ok,t +good-names=i,j,k,ex,Run,_,ok,t,tz From e85a2f548bf2d717915a01f67f5472dd2b0ee0c6 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:48:27 +0300 Subject: [PATCH 023/190] pylint: allow unittest naming unittest methods use CamelCase for its asserts. We use it here for our custom assert too. Part of #270 --- test/suites/test_encoding.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 649ff080..ae1c1ff1 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -8,6 +8,8 @@ from .lib.tarantool_server import TarantoolServer class TestSuiteEncoding(unittest.TestCase): + # pylint: disable=invalid-name + @classmethod def setUpClass(self): print(' ENCODING '.center(70, '='), file=sys.stderr) From d1880dea26d16c0ebf7f63cd8d4662b58321749f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:49:09 +0300 Subject: [PATCH 024/190] pylint: allow dbapi constants Lowercase constants are the part of dbapi, we cannot rename them. Part of #270 --- tarantool/dbapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index f6f5d2ab..51a60017 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -7,7 +7,7 @@ from tarantool.connection import Connection as BaseConnection from tarantool.error import * - +# pylint: disable=invalid-name paramstyle = 'named' apilevel = "2.0" threadsafety = 1 From 015f71d6b6f29430873473b85b0b81448be69912 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 11:38:03 +0300 Subject: [PATCH 025/190] pylint: allow sphinx constants Lowercase constants are the part of sphinx API, we cannot rename them. Part of #270 --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 83e49918..44c24173 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -8,6 +8,7 @@ # # All configuration values have a default; values that are commented out # serve to show the default. +# pylint: disable=invalid-name import sys, os From ba800d749f0345e49c1b994e694ffe0edb6b3e0a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 11:51:36 +0300 Subject: [PATCH 026/190] pylint: fix singleton-comparison cases Fix all cases of C0121 singleton-comparison. Part of #270 --- tarantool/connection_pool.py | 4 +-- test/suites/__init__.py | 56 ++++++++++++++++++------------------ test/suites/test_crud.py | 2 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 44102c57..313db449 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -240,7 +240,7 @@ def build(self): any_pool.append(key) - if state.read_only == False: + if state.read_only is False: rw_pool.append(key) else: ro_pool.append(key) @@ -620,7 +620,7 @@ def is_closed(self): :rtype: :obj:`bool` """ - return all(unit.request_processing_enabled == False for unit in self.pool.values()) + return all(unit.request_processing_enabled is False for unit in self.pool.values()) def _request_process_loop(self, key, unit, last_refresh): """ diff --git a/test/suites/__init__.py b/test/suites/__init__.py index c8fa35d7..2768e143 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -4,35 +4,35 @@ __tmp = os.getcwd() os.chdir(os.path.abspath(os.path.dirname(__file__))) -from .test_schema import TestSuite_Schema_UnicodeConnection -from .test_schema import TestSuite_Schema_BinaryConnection -from .test_dml import TestSuite_Request -from .test_protocol import TestSuite_Protocol -from .test_reconnect import TestSuite_Reconnect -from .test_mesh import TestSuite_Mesh -from .test_pool import TestSuite_Pool -from .test_execute import TestSuite_Execute -from .test_dbapi import TestSuite_DBAPI -from .test_encoding import TestSuite_Encoding -from .test_ssl import TestSuite_Ssl -from .test_decimal import TestSuite_Decimal -from .test_uuid import TestSuite_UUID -from .test_datetime import TestSuite_Datetime -from .test_interval import TestSuite_Interval -from .test_package import TestSuite_Package -from .test_error_ext import TestSuite_ErrorExt -from .test_push import TestSuite_Push -from .test_connection import TestSuite_Connection -from .test_crud import TestSuite_Crud +from .test_schema import TestSuiteSchemaUnicodeConnection +from .test_schema import TestSuiteSchemaBinaryConnection +from .test_dml import TestSuiteRequest +from .test_protocol import TestSuiteProtocol +from .test_reconnect import TestSuiteReconnect +from .test_mesh import TestSuiteMesh +from .test_pool import TestSuitePool +from .test_execute import TestSuiteExecute +from .test_dbapi import TestSuiteDBAPI +from .test_encoding import TestSuiteEncoding +from .test_ssl import TestSuiteSsl +from .test_decimal import TestSuiteDecimal +from .test_uuid import TestSuiteUUID +from .test_datetime import TestSuiteDatetime +from .test_interval import TestSuiteInterval +from .test_package import TestSuitePackage +from .test_error_ext import TestSuiteErrorExt +from .test_push import TestSuitePush +from .test_connection import TestSuiteConnection +from .test_crud import TestSuiteCrud -test_cases = (TestSuite_Schema_UnicodeConnection, - TestSuite_Schema_BinaryConnection, - TestSuite_Request, TestSuite_Protocol, TestSuite_Reconnect, - TestSuite_Mesh, TestSuite_Execute, TestSuite_DBAPI, - TestSuite_Encoding, TestSuite_Pool, TestSuite_Ssl, - TestSuite_Decimal, TestSuite_UUID, TestSuite_Datetime, - TestSuite_Interval, TestSuite_ErrorExt, TestSuite_Push, - TestSuite_Connection, TestSuite_Crud,) +test_cases = (TestSuiteSchemaUnicodeConnection, + TestSuiteSchemaBinaryConnection, + TestSuiteRequest, TestSuiteProtocol, TestSuiteReconnect, + TestSuiteMesh, TestSuiteExecute, TestSuiteDBAPI, + TestSuiteEncoding, TestSuitePool, TestSuiteSsl, + TestSuiteDecimal, TestSuiteUUID, TestSuiteDatetime, + TestSuiteInterval, TestSuiteErrorExt, TestSuitePush, + TestSuiteConnection, TestSuiteCrud,) def load_tests(loader, tests, pattern): suite = unittest.TestSuite() diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 2699fe7c..a2a8d8e8 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -41,7 +41,7 @@ def setUp(self): fetch_schema=False) # Time for vshard group configuration. time.sleep(1) - if self.conn.eval('return ROCKS_IMPORT_FAIL').data[0] == True: + if self.conn.eval('return ROCKS_IMPORT_FAIL').data[0] is True: raise unittest.SkipTest('The crud/vshard modules are not detected, ' + 'installation via rocks install is required ' + 'for CRUD testing purposes. You can use ' + From ca599908eafd207fca27f416a306930cc82b168a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 13:00:33 +0300 Subject: [PATCH 027/190] pylint: fix consider-using-f-string cases Fix all cases of C0209 consider-using-f-string. Part of #270 --- tarantool/connection.py | 4 +-- tarantool/connection_pool.py | 17 ++++------- tarantool/dbapi.py | 2 +- tarantool/error.py | 2 +- tarantool/mesh_connection.py | 6 ++-- tarantool/msgpack_ext/packer.py | 2 +- tarantool/msgpack_ext/unpacker.py | 2 +- tarantool/request.py | 6 ++-- tarantool/schema.py | 11 +++---- test/suites/lib/remote_tarantool_server.py | 20 +++++-------- test/suites/lib/skip.py | 8 ++--- test/suites/lib/tarantool_server.py | 18 ++++++------ test/suites/test_connection.py | 16 +++++----- test/suites/test_dbapi.py | 14 ++++----- test/suites/test_encoding.py | 34 +++++++++++----------- test/suites/test_execute.py | 8 ++--- test/suites/test_mesh.py | 22 +++++++------- test/suites/test_pool.py | 2 +- test/suites/test_schema.py | 4 +-- test/suites/test_ssl.py | 2 +- 20 files changed, 93 insertions(+), 107 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 96cd9a4c..86476a33 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1210,8 +1210,8 @@ def check(): # Check that connection is alive else: if self.connected: break - warn("Reconnecting, attempt %d of %d" % - (attempt, self.reconnect_max_attempts), NetworkWarning) + warn(f"Reconnecting, attempt {attempt} of {self.reconnect_max_attempts}", + NetworkWarning) if attempt == self.reconnect_max_attempts: raise NetworkError( socket.error(last_errno, errno.errorcode[last_errno])) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 313db449..117e6f9c 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -526,7 +526,7 @@ def _make_key(self, addr): :meta private: """ - return '{0}:{1}'.format(addr['host'], addr['port']) + return f"{addr['host']}:{addr['port']}" def _get_new_state(self, unit): """ @@ -546,24 +546,21 @@ def _get_new_state(self, unit): try: conn.connect() except NetworkError as exc: - msg = "Failed to connect to {0}:{1}".format( - unit.addr['host'], unit.addr['port']) + msg = f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}" warn(msg, ClusterConnectWarning) return InstanceState(Status.UNHEALTHY) try: resp = conn.call('box.info') except NetworkError as exc: - msg = "Failed to get box.info for {0}:{1}, reason: {2}".format( - unit.addr['host'], unit.addr['port'], repr(exc)) + msg = f"Failed to get box.info for {unit.addr['host']}:{unit.addr['port']}, reason: {repr(exc)}" warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) try: read_only = resp.data[0]['ro'] except (IndexError, KeyError) as exc: - msg = "Incorrect box.info response from {0}:{1}".format( - unit.addr['host'], unit.addr['port']) + msg = f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) @@ -571,13 +568,11 @@ def _get_new_state(self, unit): status = resp.data[0]['status'] if status != 'running': - msg = "{0}:{1} instance status is not 'running'".format( - unit.addr['host'], unit.addr['port']) + msg = f"{unit.addr['host']}:{unit.addr['port']} instance status is not 'running'" warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) except (IndexError, KeyError) as exc: - msg = "Incorrect box.info response from {0}:{1}".format( - unit.addr['host'], unit.addr['port']) + msg = f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 51a60017..88e29d32 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -291,7 +291,7 @@ def _set_autocommit(self, autocommit): if not isinstance(autocommit, bool): raise InterfaceError("autocommit parameter must be boolean, " - "not %s" % autocommit.__class__.__name__) + f"not {autocommit.__class__.__name__}") if autocommit is False: raise NotSupportedError("The connector supports " "only autocommit mode") diff --git a/tarantool/error.py b/tarantool/error.py index 7b4181cd..cd981c21 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -194,7 +194,7 @@ def os_strerror_patched(code): if not message.startswith("Unknown"): return message else: - return _code2str.get(code, "Unknown error %s" % code) + return _code2str.get(code, f"Unknown error {code}") os.strerror = os_strerror_patched del os_strerror_patched diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 38da6dba..535e6412 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -61,7 +61,7 @@ def parse_uri(uri): # TODO: Support Unix sockets. def parse_error(uri, msg): - msg = 'URI "%s": %s' % (uri, msg) + msg = f'URI "{uri}": {msg}' return None, msg if not uri: @@ -119,7 +119,7 @@ def prepare_address(address): """ def format_error(address, err): - return None, 'Address %s: %s' % (str(address), err) + return None, f'Address {str(address)}: {err}' if not isinstance(address, dict): return format_error(address, 'address must be a dict') @@ -559,7 +559,7 @@ def _opt_refresh_instances(self): try: resp = self._send_request_wo_reconnect(request) except DatabaseError as exc: - msg = 'got "%s" error, skipped address updates' % str(exc) + msg = f'got "{str(exc)}" error, skipped address updates' warn(msg, ClusterDiscoveryWarning) return diff --git a/tarantool/msgpack_ext/packer.py b/tarantool/msgpack_ext/packer.py index 12faa29e..ebafb10c 100644 --- a/tarantool/msgpack_ext/packer.py +++ b/tarantool/msgpack_ext/packer.py @@ -48,4 +48,4 @@ def default(obj, packer=None): for encoder in encoders: if isinstance(obj, encoder['type']): return ExtType(encoder['ext'].EXT_ID, encoder['ext'].encode(obj, packer)) - raise TypeError("Unknown type: %r" % (obj,)) + raise TypeError(f"Unknown type: {repr(obj)}") diff --git a/tarantool/msgpack_ext/unpacker.py b/tarantool/msgpack_ext/unpacker.py index 6950f485..956df6ca 100644 --- a/tarantool/msgpack_ext/unpacker.py +++ b/tarantool/msgpack_ext/unpacker.py @@ -42,4 +42,4 @@ def ext_hook(code, data, unpacker=None): if code in decoders: return decoders[code](data, unpacker) - raise NotImplementedError("Unknown msgpack extension type code %d" % (code,)) + raise NotImplementedError(f"Unknown msgpack extension type code {code}") diff --git a/tarantool/request.py b/tarantool/request.py index 05771b0f..2b7d672f 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -683,10 +683,10 @@ def __init__(self, conn, sql, args): super(RequestExecute, self).__init__(conn) if isinstance(args, Mapping): - args = [{":%s" % name: value} for name, value in args.items()] + args = [{f":{name}": value} for name, value in args.items()] elif not isinstance(args, Sequence): - raise TypeError("Parameter type '%s' is not supported. " - "Must be a mapping or sequence" % type(args)) + raise TypeError(f"Parameter type '{type(args)}' is not supported. " + "Must be a mapping or sequence") request_body = self._dumps({IPROTO_SQL_TEXT: sql, IPROTO_SQL_BIND: args}) diff --git a/tarantool/schema.py b/tarantool/schema.py index fb000333..2ed1a0ea 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -244,7 +244,7 @@ def fetch_space(self, space): elif len(space_row) == 0 or not len(space_row[0]): # We can't find space with this name or id temp_name = 'name' if isinstance(space, str) else 'id' - errmsg = "There's no space with {1} '{0}'".format(space, temp_name) + errmsg = f"There's no space with {temp_name} '{space}'" raise SchemaError(errmsg) space_row = space_row[0] @@ -356,9 +356,8 @@ def fetch_index(self, space_object, index): elif len(index_row) == 0 or not len(index_row[0]): # We can't find index with this name or id temp_name = 'name' if isinstance(index, str) else 'id' - errmsg = ("There's no index with {2} '{0}'" - " in space '{1}'").format(index, space_object.name, - temp_name) + errmsg = (f"There's no index with {temp_name} '{index}'" + f" in space '{space_object.name}'") raise SchemaError(errmsg) index_row = index_row[0] @@ -452,9 +451,7 @@ def get_field(self, space, field): return _space.format[field] except: kind = 'name' if isinstance(field, str) else 'id' - errmsg = "There's no field with {2} '{0}' in space '{1}'".format( - field, _space.name, kind - ) + errmsg = f"There's no field with {kind} '{field}' in space '{_space.name}'" raise SchemaError(errmsg) return field diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index 5d6afd77..f1049c60 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -41,42 +41,38 @@ def __init__(self): def acquire_lock(self): deadline = time.time() + AWAIT_TIME while True: - res = self.admin.execute('return acquire_lock("%s")' % self.whoami) + res = self.admin.execute(f'return acquire_lock("{ self.whoami}")') ok = res[0] err = res[1] if not ok else None if ok: break if time.time() > deadline: - raise RuntimeError('can not acquire "%s" lock: %s' % ( - self.whoami, str(err))) - print('waiting to acquire "%s" lock' % self.whoami, + raise RuntimeError(f'can not acquire "{self.whoami}" lock: {str(err)}') + print(f'waiting to acquire "{self.whoami}" lock', file=sys.stderr) time.sleep(1) self.lock_is_acquired = True def touch_lock(self): assert(self.lock_is_acquired) - res = self.admin.execute('return touch_lock("%s")' % self.whoami) + res = self.admin.execute(f'return touch_lock("{self.whoami}")') ok = res[0] err = res[1] if not ok else None if not ok: - raise RuntimeError('can not update "%s" lock: %s' % ( - self.whoami, str(err))) + raise RuntimeError(f'can not update "{self.whoami}" lock: {str(err)}') def release_lock(self): - res = self.admin.execute('return release_lock("%s")' % self.whoami) + res = self.admin.execute(f'return release_lock("{self.whoami}")') ok = res[0] err = res[1] if not ok else None if not ok: - raise RuntimeError('can not release "%s" lock: %s' % ( - self.whoami, str(err))) + raise RuntimeError(f'can not release "{self.whoami}" lock: {str(err)}') self.lock_is_acquired = False def start(self): if not self.lock_is_acquired: self.acquire_lock() - self.admin.execute('box.cfg{listen = "0.0.0.0:%s"}' % - self.args['primary']) + self.admin.execute(f'box.cfg{{listen = "0.0.0.0:{self.args["primary"]}"}}') def stop(self): self.admin.execute('box.cfg{listen = box.NULL}') diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 7cb222e6..bb559873 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -34,7 +34,7 @@ def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): support_version = pkg_resources.parse_version(required_tt_version) if self.tnt_version < support_version: - self.skipTest('Tarantool %s %s' % (self.tnt_version, msg)) + self.skipTest(f'Tarantool {self.tnt_version} {msg}') def skip_or_run_test_tarantool(func, required_tt_version, msg): @@ -92,9 +92,9 @@ def wrapper(self, *args, **kwargs): assert srv is not None - resp = srv.admin("pcall(require, '%s')" % required_tt_module) + resp = srv.admin(f"pcall(require, '{required_tt_module}')") if not resp[0]: - self.skipTest('Tarantool %s' % (msg, )) + self.skipTest(f'Tarantool {msg}') if func.__name__ != 'setUp': func(self, *args, **kwargs) @@ -119,7 +119,7 @@ def wrapper(self, *args, **kwargs): python_version = pkg_resources.parse_version(python_version_str) support_version = pkg_resources.parse_version(required_python_version) if python_version < support_version: - self.skipTest('Python %s connector %s' % (python_version, msg)) + self.skipTest(f'Python {python_version} connector {msg}') if func.__name__ != 'setUp': func(self, *args, **kwargs) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index ffff1320..d67f6243 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -25,7 +25,7 @@ def check_port(port, rais=True): sock.close() if rais: - raise RuntimeError("The server is already running on port {0}".format(port)) + raise RuntimeError(f"The server is already running on port {port}") return False @@ -100,7 +100,7 @@ def _admin(self, port): try: int(port) except ValueError: - raise ValueError("Bad port number: '%s'" % port) + raise ValueError(f"Bad port number: '{port}'") if hasattr(self, 'admin'): del self.admin self.admin = TarantoolAdmin('0.0.0.0', port) @@ -186,17 +186,17 @@ def generate_listen(self, port, port_only): if not port_only and self.transport == SSL_TRANSPORT: listen = self.host + ":" + str(port) + "?transport=ssl&" if self.ssl_key_file: - listen += "ssl_key_file={}&".format(self.ssl_key_file) + listen += f"ssl_key_file={self.ssl_key_file}&" if self.ssl_cert_file: - listen += "ssl_cert_file={}&".format(self.ssl_cert_file) + listen += f"ssl_cert_file={self.ssl_cert_file}&" if self.ssl_ca_file: - listen += "ssl_ca_file={}&".format(self.ssl_ca_file) + listen += f"ssl_ca_file={self.ssl_ca_file}&" if self.ssl_ciphers: - listen += "ssl_ciphers={}&".format(self.ssl_ciphers) + listen += f"ssl_ciphers={self.ssl_ciphers}&" if self.ssl_password: - listen += "ssl_password={}&".format(self.ssl_password) + listen += f"ssl_password={self.ssl_password}&" if self.ssl_password_file: - listen += "ssl_password_file={}&".format(self.ssl_password_file) + listen += f"ssl_password_file={self.ssl_password_file}&" listen = listen[:-1] else: listen = str(port) @@ -234,7 +234,7 @@ def wait_until_started(self): elif ans in ('loading',): continue else: - raise Exception("Strange output for `box.info.status`: %s" % (ans)) + raise Exception(f"Strange output for `box.info.status`: {ans}") except socket.error as exc: if exc.errno == errno.ECONNREFUSED: time.sleep(0.1) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 3cd46aba..6492d77e 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -61,7 +61,7 @@ def my_ext_type_encoder(obj): if isinstance(obj, decimal.Decimal): obj = obj + 1 return msgpack.ExtType(ext_decimal.EXT_ID, ext_decimal.encode(obj, None)) - raise TypeError("Unknown type: %r" % (obj,)) + raise TypeError(f"Unknown type: {repr(obj)}") def my_packer_factory(_): return msgpack.Packer(default=my_ext_type_encoder) @@ -92,7 +92,7 @@ def test_custom_unpacker(self): def my_ext_type_decoder(code, data): if code == ext_decimal.EXT_ID: return ext_decimal.decode(data, None) - 1 - raise NotImplementedError("Unknown msgpack extension type code %d" % (code,)) + raise NotImplementedError(f"Unknown msgpack extension type code {code}") def my_unpacker_factory(_): if msgpack.version >= (1, 0, 0): @@ -127,13 +127,13 @@ def my_unpacker_factory(_): data = bytes(bytearray.fromhex(data_hex)) space = 'space_varbin' - self.con.execute(""" - INSERT INTO "%s" VALUES (%d, x'%s'); - """ % (space, data_id, data_hex)) + self.con.execute(f""" + INSERT INTO "{space}" VALUES ({data_id}, x'{data_hex}'); + """) - resp = self.con.execute(""" - SELECT * FROM "%s" WHERE "varbin" == x'%s'; - """ % (space, data_hex)) + resp = self.con.execute(f""" + SELECT * FROM "{space}" WHERE "varbin" == x'{data_hex}'; + """) self.assertSequenceEqual(resp, [[data_id, data]]) def test_custom_unpacker_supersedes_use_list(self): diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index e2efe571..638c79ff 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -12,11 +12,11 @@ class TestSuiteDBAPI(dbapi20.DatabaseAPI20Test): table_prefix = 'dbapi20test_' # If you need to specify a prefix for tables - ddl0 = 'create table %s (id INTEGER PRIMARY KEY AUTOINCREMENT, ' \ + ddl0 = f'create table {table_prefix} (id INTEGER PRIMARY KEY AUTOINCREMENT, ' \ 'name varchar(20))' - ddl1 = 'create table %sbooze (name varchar(20) primary key)' % table_prefix - ddl2 = 'create table %sbarflys (name varchar(20) primary key, ' \ - 'drink varchar(30))' % table_prefix + ddl1 = f'create table {table_prefix}booze (name varchar(20) primary key)' + ddl2 = f'create table {table_prefix}barflys (name varchar(20) primary key, ' \ + 'drink varchar(30))' @classmethod def setUpClass(self): @@ -57,14 +57,12 @@ def test_rowcount(self): 'cursor.rowcount should be -1 or 1 after executing no-result ' 'statements' + str(cur.rowcount) ) - cur.execute("%s into %sbooze values ('Victoria Bitter')" % ( - self.insert, self.table_prefix - )) + cur.execute(f"{self.insert} into {self.table_prefix}booze values ('Victoria Bitter')") dbapi20._failUnless(self,cur.rowcount == 1, 'cursor.rowcount should == number or rows inserted, or ' 'set to -1 after executing an insert statement' ) - cur.execute("select name from %sbooze" % self.table_prefix) + cur.execute(f"select name from {self.table_prefix}booze") dbapi20._failUnless(self,cur.rowcount == -1, 'cursor.rowcount should == number of rows returned, or ' 'set to -1 after executing a select statement' diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index ae1c1ff1..9394515f 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -69,7 +69,7 @@ def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) except Exception as exc: - self.fail('Function raised Exception: %s' % repr(exc)) + self.fail(f'Function raised Exception: {repr(exc)}') def setUp(self): # prevent a remote tarantool from clean our session @@ -94,9 +94,9 @@ def test_01_02_string_decode_for_encoding_utf8_behavior(self): data = 'test_01_02' space = 'space_str' - self.srv.admin("box.space['%s']:insert{'%s'}" % (space, data)) + self.srv.admin(f"box.space['{space}']:insert{{'{data}'}}") - resp = self.con_encoding_utf8.eval("return box.space['%s']:get('%s')" % (space, data)) + resp = self.con_encoding_utf8.eval(f"return box.space['{space}']:get('{data}')") self.assertSequenceEqual(resp, [[data]]) @skip_or_run_varbinary_test @@ -117,13 +117,13 @@ def test_01_04_varbinary_decode_for_encoding_utf8_behavior(self): data = bytes(bytearray.fromhex(data_hex)) space = 'space_varbin' - self.con_encoding_utf8.execute(""" - INSERT INTO "%s" VALUES (%d, x'%s'); - """ % (space, data_id, data_hex)) + self.con_encoding_utf8.execute(f""" + INSERT INTO "{space}" VALUES ({data_id}, x'{data_hex}'); + """) - resp = self.con_encoding_utf8.execute(""" - SELECT * FROM "%s" WHERE "varbin" == x'%s'; - """ % (space, data_hex)) + resp = self.con_encoding_utf8.execute(f""" + SELECT * FROM "{space}" WHERE "varbin" == x'{data_hex}'; + """) self.assertSequenceEqual(resp, [[data_id, data]]) # encoding = None @@ -146,9 +146,9 @@ def test_02_02_string_decode_for_encoding_none_behavior(self): data_decoded = b'test_02_02' space = 'space_str' - self.srv.admin("box.space['%s']:insert{'%s'}" % (space, data)) + self.srv.admin(f"box.space['{space}']:insert{{'{data}'}}") - resp = self.con_encoding_none.eval("return box.space['%s']:get('%s')" % (space, data)) + resp = self.con_encoding_none.eval(f"return box.space['{space}']:get('{data}')") self.assertSequenceEqual(resp, [[data_decoded]]) def test_02_03_bytes_encode_for_encoding_none_behavior(self): @@ -167,13 +167,13 @@ def test_02_04_varbinary_decode_for_encoding_none_behavior(self): data = bytes(bytearray.fromhex(data_hex)) space = 'space_varbin' - self.con_encoding_none.execute(""" - INSERT INTO "%s" VALUES (%d, x'%s'); - """ % (space, data_id, data_hex)) + self.con_encoding_none.execute(f""" + INSERT INTO "{space}" VALUES ({data_id}, x'{data_hex}'); + """) - resp = self.con_encoding_none.execute(""" - SELECT * FROM "%s" WHERE "varbin" == x'%s'; - """ % (space, data_hex)) + resp = self.con_encoding_none.execute(f""" + SELECT * FROM "{space}" WHERE "varbin" == x'{data_hex}'; + """) self.assertSequenceEqual(resp, [[data_id, data]]) @skip_or_run_error_extra_info_test diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 67e82288..40c5029e 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -45,7 +45,7 @@ def tearDownClass(self): self.srv.clean() def _populate_data(self, table_name): - query = "insert into %s values (:id, :name)" % table_name + query = f"insert into {table_name} values (:id, :name)" for param in self.dml_params: self.con.execute(query, param) @@ -59,7 +59,7 @@ def test_dml_response(self): self.assertEqual(response.affected_row_count, 1) self.assertEqual(response.data, None) - query = "insert into %s values (:id, :name)" % table_name + query = f"insert into {table_name} values (:id, :name)" for num, param in enumerate(self.dml_params, start=1): response = self.con.execute(query, param) @@ -67,7 +67,7 @@ def test_dml_response(self): self.assertEqual(response.affected_row_count, 1) self.assertEqual(response.data, None) - query = "delete from %s where id in (4, 5)" % table_name + query = f"delete from {table_name} where id in (4, 5)" response = self.con.execute(query) self.assertEqual(response.autoincrement_ids, None) self.assertEqual(response.affected_row_count, 2) @@ -78,7 +78,7 @@ def test_dql_response(self): self._create_table(table_name) self._populate_data(table_name) - select_query = "select name from %s where id in (1, 3, 5)" % table_name + select_query = f"select name from {table_name} where id in (1, 3, 5)" response = self.con.execute(select_query) self.assertEqual(response.autoincrement_ids, None) self.assertEqual(response.affected_row_count, None) diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index 2fd87bd7..d3a8a402 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -21,7 +21,7 @@ def create_server(_id): srv.admin("box.schema.user.grant('test', 'execute', 'universe')") # Create srv_id function (for testing purposes). - srv.admin("function srv_id() return %s end" % _id) + srv.admin(f"function srv_id() return {_id} end") return srv @@ -30,21 +30,21 @@ def create_server(_id): class TestSuiteMesh(unittest.TestCase): def define_cluster_function(self, func_name, servers): addresses = [(srv.host, srv.args['primary']) for srv in servers] - addresses_lua = ",".join("'%s:%d'" % address for address in addresses) - func_body = """ - function %s() - return {%s} + addresses_lua = ",".join(f"'{address[0]}:{address[1]}'" for address in addresses) + func_body = f""" + function {func_name}() + return {{{addresses_lua}}} end - """ % (func_name, addresses_lua) + """ for srv in self.servers: srv.admin(func_body) def define_custom_cluster_function(self, func_name, retval): - func_body = """ - function %s() - return %s + func_body = f""" + function {func_name}() + return {retval} end - """ % (func_name, retval) + """ for srv in self.servers: srv.admin(func_body) @@ -173,7 +173,7 @@ def test_02_discovery_bad_address(self): def test_03_discovery_bad_good_addresses(self): func_name = 'bad_and_good_addresses' - retval = "{'localhost:', '%s:%d'}" % (self.host_2, self.port_2) + retval = f"{{'localhost:', '{self.host_2}:{self.port_2}'}}" self.define_custom_cluster_function(func_name, retval) con = tarantool.MeshConnection(self.host_1, self.port_1, user='test', password='test') diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index bdb7284a..b5b59dfa 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -39,7 +39,7 @@ def create_server(_id): srv.admin("json = require('json')") # Create srv_id function (for testing purposes). - srv.admin("function srv_id() return %s end" % _id) + srv.admin(f"function srv_id() return {_id} end") return srv diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 1607b621..4201d579 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -39,8 +39,8 @@ class TestSuiteSchemaAbstract(unittest.TestCase): @classmethod def setUpClass(self): - params = 'connection.encoding: {}'.format(repr(self.encoding)) - print(' SCHEMA ({}) '.format(params).center(70, '='), file=sys.stderr) + params = f'connection.encoding: {repr(self.encoding)}' + print(f' SCHEMA ({params}) '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) self.srv = TarantoolServer() self.srv.script = 'test/suites/box.lua' diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 23c89cc7..fda0f7c8 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -721,7 +721,7 @@ def test_mesh(self): srv.admin(""" box.schema.user.grant('test', 'execute,read,write', 'universe') """.replace('\n', ' ')) - srv.admin("function srv_id() return %s end" % i) + srv.admin(f"function srv_id() return {i} end") servers.append(srv) addr = { 'host': srv.host, From 3be82cc5765c523ea58e1cdea4f55840af5850ab Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 13:31:38 +0300 Subject: [PATCH 028/190] pylint: fix consider-using-dict-items cases Fix all cases of C0206 consider-using-dict-items. Part of #270 --- tarantool/connection_pool.py | 4 +-- tarantool/msgpack_ext/interval.py | 3 +- tarantool/msgpack_ext/types/interval.py | 3 +- test/suites/lib/skip.py | 2 +- test/suites/test_crud.py | 21 ++++++------ test/suites/test_datetime.py | 43 +++++++------------------ test/suites/test_decimal.py | 32 +++++------------- test/suites/test_error_ext.py | 12 +++---- test/suites/test_interval.py | 32 +++++------------- test/suites/test_push.py | 21 ++++++------ test/suites/test_uuid.py | 16 +++------ 11 files changed, 57 insertions(+), 132 deletions(-) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 117e6f9c..dd49d0f6 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -663,9 +663,7 @@ def connect(self): and refresh the info would be processed in the background. """ - for key in self.pool: - unit = self.pool[key] - + for key, unit in self.pool.items(): self._refresh_state(key) last_refresh = time.time() diff --git a/tarantool/msgpack_ext/interval.py b/tarantool/msgpack_ext/interval.py index 9fd26682..a45a8213 100644 --- a/tarantool/msgpack_ext/interval.py +++ b/tarantool/msgpack_ext/interval.py @@ -65,8 +65,7 @@ def encode(obj, _): buf = bytes() count = 0 - for field_id in id_map.keys(): - field_name = id_map[field_id] + for field_id, field_name in id_map.items(): value = getattr(obj, field_name) if field_name == 'adjust': diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index 0ccb6f0e..66679b92 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -219,8 +219,7 @@ def __eq__(self, other): # - false # ... - for field_id in id_map.keys(): - field_name = id_map[field_id] + for _, field_name in id_map.items(): if getattr(self, field_name) != getattr(other, field_name): return False diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index bb559873..4b464caa 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -115,7 +115,7 @@ def wrapper(self, *args, **kwargs): func(self, *args, **kwargs) ver = sys.version_info - python_version_str = '%d.%d' % (ver.major, ver.minor) + python_version_str = f'{ver.major}.{ver.minor}' python_version = pkg_resources.parse_version(python_version_str) support_version = pkg_resources.parse_version(required_python_version) if python_version < support_version: diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index a2a8d8e8..9ac56b33 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -716,30 +716,27 @@ def _exception_operation_with_crud(self, testing_function, case, mode=None): self.assertEqual(exc.success_list.rows, case['error']['output']['res_rows']) def test_crud_module_via_connection(self): - for case_name in self.crud_test_cases.keys(): - with self.subTest(name=case_name): - case = self.crud_test_cases[case_name] - testing_function = getattr(self.conn, case_name) + for name, case in self.crud_test_cases.items(): + with self.subTest(name=name): + testing_function = getattr(self.conn, name) # Correct try testing. self._correct_operation_with_crud(testing_function, case) # Exception try testing. self._exception_operation_with_crud(testing_function, case) def test_crud_module_via_mesh_connection(self): - for case_name in self.crud_test_cases.keys(): - with self.subTest(name=case_name): - case = self.crud_test_cases[case_name] - testing_function = getattr(self.conn_mesh, case_name) + for name, case in self.crud_test_cases.items(): + with self.subTest(name=name): + testing_function = getattr(self.conn_mesh, name) # Correct try testing. self._correct_operation_with_crud(testing_function, case) # Exception try testing. self._exception_operation_with_crud(testing_function, case) def test_crud_module_via_pool_connection(self): - for case_name in self.crud_test_cases.keys(): - with self.subTest(name=case_name): - case = self.crud_test_cases[case_name] - testing_function = getattr(self.conn_pool, case_name) + for name, case in self.crud_test_cases.items(): + with self.subTest(name=name): + testing_function = getattr(self.conn_pool, name) # Correct try testing. self._correct_operation_with_crud(testing_function, case, mode=tarantool.Mode.RW) # Exception try testing. diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 313d49cc..5062c3f9 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -140,9 +140,8 @@ def test_datetime_class_api_wth_tz(self): } def test_datetime_class_invalid_init(self): - for name in self.datetime_class_invalid_init_cases.keys(): + for name, case in self.datetime_class_invalid_init_cases.items(): with self.subTest(msg=name): - case = self.datetime_class_invalid_init_cases[name] self.assertRaisesRegex( case['type'], re.escape(case['msg']), lambda: tarantool.Datetime(*case['args'], **case['kwargs'])) @@ -279,38 +278,30 @@ def test_datetime_class_invalid_init(self): } def test_msgpack_decode(self): - for name in self.integration_cases.keys(): + for name, case in self.integration_cases.items(): with self.subTest(msg=name): - case = self.integration_cases[name] - self.assertEqual(unpacker_ext_hook(4, case['msgpack']), case['python']) @skip_or_run_datetime_test def test_tarantool_decode(self): - for name in self.integration_cases.keys(): + for name, case in self.integration_cases.items(): with self.subTest(msg=name): - case = self.integration_cases[name] - self.adm(f"box.space['test']:replace{{'{name}', {case['tarantool']}, 'field'}}") self.assertSequenceEqual(self.con.select('test', name), [[name, case['python'], 'field']]) def test_msgpack_encode(self): - for name in self.integration_cases.keys(): + for name, case in self.integration_cases.items(): with self.subTest(msg=name): - case = self.integration_cases[name] - self.assertEqual(packer_default(case['python']), msgpack.ExtType(code=4, data=case['msgpack'])) @skip_or_run_datetime_test def test_tarantool_encode(self): - for name in self.integration_cases.keys(): + for name, case in self.integration_cases.items(): with self.subTest(msg=name): - case = self.integration_cases[name] - self.con.insert('test', [name, case['python'], 'field']) lua_eval = f""" @@ -370,18 +361,14 @@ def test_msgpack_decode_ambiguous_tzindex(self): } def test_python_datetime_subtraction(self): - for name in self.datetime_subtraction_cases.keys(): + for name, case in self.datetime_subtraction_cases.items(): with self.subTest(msg=name): - case = self.datetime_subtraction_cases[name] - self.assertEqual(case['arg_1'] - case['arg_2'], case['res']) @skip_or_run_datetime_test def test_tarantool_datetime_subtraction(self): - for name in self.datetime_subtraction_cases.keys(): + for name, case in self.datetime_subtraction_cases.items(): with self.subTest(msg=name): - case = self.datetime_subtraction_cases[name] - self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res']]) @@ -490,34 +477,26 @@ def test_tarantool_datetime_subtraction_different_timezones(self): } def test_python_datetime_addition(self): - for name in self.interval_arithmetic_cases.keys(): + for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): - case = self.interval_arithmetic_cases[name] - self.assertEqual(case['arg_1'] + case['arg_2'], case['res_add']) def test_python_datetime_subtraction(self): - for name in self.interval_arithmetic_cases.keys(): + for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): - case = self.interval_arithmetic_cases[name] - self.assertEqual(case['arg_1'] - case['arg_2'], case['res_sub']) @skip_or_run_datetime_test def test_tarantool_datetime_addition(self): - for name in self.interval_arithmetic_cases.keys(): + for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): - case = self.interval_arithmetic_cases[name] - self.assertSequenceEqual(self.con.call('add', case['arg_1'], case['arg_2']), [case['res_add']]) @skip_or_run_datetime_test def test_tarantool_datetime_subtraction(self): - for name in self.interval_arithmetic_cases.keys(): + for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): - case = self.interval_arithmetic_cases[name] - self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res_sub']]) diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 90052edc..42ae8746 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -224,19 +224,15 @@ def setUp(self): } def test_msgpack_decode(self): - for name in self.valid_cases.keys(): + for name, case in self.valid_cases.items(): with self.subTest(msg=name): - case = self.valid_cases[name] - self.assertEqual(unpacker_ext_hook(1, case['msgpack']), case['python']) @skip_or_run_decimal_test def test_tarantool_decode(self): - for name in self.valid_cases.keys(): + for name, case in self.valid_cases.items(): with self.subTest(msg=name): - case = self.valid_cases[name] - self.adm(f"box.space['test']:replace{{'{name}', {case['tarantool']}}}") self.assertSequenceEqual( @@ -244,19 +240,15 @@ def test_tarantool_decode(self): [[name, case['python']]]) def test_msgpack_encode(self): - for name in self.valid_cases.keys(): + for name, case in self.valid_cases.items(): with self.subTest(msg=name): - case = self.valid_cases[name] - self.assertEqual(packer_default(case['python']), msgpack.ExtType(code=1, data=case['msgpack'])) @skip_or_run_decimal_test def test_tarantool_encode(self): - for name in self.valid_cases.keys(): + for name, case in self.valid_cases.items(): with self.subTest(msg=name): - case = self.valid_cases[name] - self.con.insert('test', [name, case['python']]) lua_eval = f""" @@ -303,10 +295,8 @@ def test_tarantool_encode(self): } def test_msgpack_encode_error(self): - for name in self.error_cases.keys(): + for name, case in self.error_cases.items(): with self.subTest(msg=name): - case = self.error_cases[name] - msg = 'Decimal cannot be encoded: Tarantool decimal ' + \ 'supports a maximum of 38 digits.' self.assertRaisesRegex( @@ -315,10 +305,8 @@ def test_msgpack_encode_error(self): @skip_or_run_decimal_test def test_tarantool_encode_error(self): - for name in self.error_cases.keys(): + for name, case in self.error_cases.items(): with self.subTest(msg=name): - case = self.error_cases[name] - msg = 'Decimal cannot be encoded: Tarantool decimal ' + \ 'supports a maximum of 38 digits.' self.assertRaisesRegex( @@ -384,10 +372,8 @@ def test_tarantool_encode_error(self): } def test_msgpack_encode_with_precision_loss(self): - for name in self.precision_loss_cases.keys(): + for name, case in self.precision_loss_cases.items(): with self.subTest(msg=name): - case = self.precision_loss_cases[name] - msg = 'Decimal encoded with loss of precision: ' + \ 'Tarantool decimal supports a maximum of 38 digits.' @@ -402,10 +388,8 @@ def test_msgpack_encode_with_precision_loss(self): @skip_or_run_decimal_test def test_tarantool_encode_with_precision_loss(self): - for name in self.precision_loss_cases.keys(): + for name, case in self.precision_loss_cases.items(): with self.subTest(msg=name): - case = self.precision_loss_cases[name] - msg = 'Decimal encoded with loss of precision: ' + \ 'Tarantool decimal supports a maximum of 38 digits.' diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 4831830f..c77cb421 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -262,9 +262,8 @@ def setUp(self): def test_msgpack_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] conn = getattr(self, case['conn']) self.assertEqual( @@ -277,9 +276,8 @@ def test_msgpack_decode(self): @skip_or_run_error_ext_type_test def test_tarantool_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] conn = getattr(self, case['conn']) self.adm(f""" @@ -323,9 +321,8 @@ def test_tarantool_decode(self): def test_msgpack_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] conn = getattr(self, case['conn']) self.assertEqual(packer_default(case['python'], conn._packer_factory()), @@ -333,9 +330,8 @@ def test_msgpack_encode(self): @skip_or_run_error_ext_type_test def test_tarantool_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] conn = getattr(self, case['conn']) conn.insert( diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 659719e1..5affbd76 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -146,10 +146,8 @@ def test_interval_positional_init(self): } def test_msgpack_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.assertEqual(unpacker_ext_hook( 6, case['msgpack'], @@ -159,29 +157,23 @@ def test_msgpack_decode(self): @skip_or_run_datetime_test def test_tarantool_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.adm(f"box.space['test']:replace{{'{name}', {case['tarantool']}, 'field'}}") self.assertSequenceEqual(self.con.select('test', name), [[name, case['python'], 'field']]) def test_msgpack_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.assertEqual(packer_default(case['python']), msgpack.ExtType(code=6, data=case['msgpack'])) @skip_or_run_datetime_test def test_tarantool_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.con.insert('test', [name, case['python'], 'field']) lua_eval = f""" @@ -272,34 +264,26 @@ def test_unknown_adjust_decode(self): } def test_python_interval_addition(self): - for name in self.arithmetic_cases.keys(): + for name, case in self.arithmetic_cases.items(): with self.subTest(msg=name): - case = self.arithmetic_cases[name] - self.assertEqual(case['arg_1'] + case['arg_2'], case['res_add']) def test_python_interval_subtraction(self): - for name in self.arithmetic_cases.keys(): + for name, case in self.arithmetic_cases.items(): with self.subTest(msg=name): - case = self.arithmetic_cases[name] - self.assertEqual(case['arg_1'] - case['arg_2'], case['res_sub']) @skip_or_run_datetime_test def test_tarantool_interval_addition(self): - for name in self.arithmetic_cases.keys(): + for name, case in self.arithmetic_cases.items(): with self.subTest(msg=name): - case = self.arithmetic_cases[name] - self.assertSequenceEqual(self.con.call('add', case['arg_1'], case['arg_2']), [case['res_add']]) @skip_or_run_datetime_test def test_tarantool_interval_subtraction(self): - for name in self.arithmetic_cases.keys(): + for name, case in self.arithmetic_cases.items(): with self.subTest(msg=name): - case = self.arithmetic_cases[name] - self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res_sub']]) diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 21f822e5..18e71a80 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -199,11 +199,10 @@ def setUp(self): } def test_00_00_push_via_connection(self): - for case_name in self.push_test_cases.keys(): - with self.subTest(name=case_name): + for name, case in self.push_test_cases.items(): + with self.subTest(name=name): callback_res = [] - case = self.push_test_cases[case_name] - testing_function = getattr(self.conn, case_name) + testing_function = getattr(self.conn, name) case['input']['kwargs']['on_push_ctx'] = callback_res resp = testing_function( *case['input']['args'], @@ -214,11 +213,10 @@ def test_00_00_push_via_connection(self): self.assertEqual(resp.data[0], case['output']['resp']) def test_00_01_push_via_mesh_connection(self): - for case_name in self.push_test_cases.keys(): - with self.subTest(name=case_name): + for name, case in self.push_test_cases.items(): + with self.subTest(name=name): callback_res = [] - case = self.push_test_cases[case_name] - testing_function = getattr(self.mesh_conn, case_name) + testing_function = getattr(self.mesh_conn, name) case['input']['kwargs']['on_push_ctx'] = callback_res resp = testing_function( *case['input']['args'], @@ -229,11 +227,10 @@ def test_00_01_push_via_mesh_connection(self): self.assertEqual(resp.data[0], case['output']['resp']) def test_00_02_push_via_connection_pool(self): - for case_name in self.push_test_cases.keys(): - with self.subTest(name=case_name): + for name, case in self.push_test_cases.items(): + with self.subTest(name=name): callback_res = [] - case = self.push_test_cases[case_name] - testing_function = getattr(self.conn_pool, case_name) + testing_function = getattr(self.conn_pool, name) case['input']['kwargs']['on_push_ctx'] = callback_res resp = testing_function( *case['input']['args'], diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index f00c544e..299c1161 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -83,38 +83,30 @@ def setUp(self): } def test_msgpack_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.assertEqual(unpacker_ext_hook(2, case['msgpack']), case['python']) @skip_or_run_uuid_test def test_tarantool_decode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.adm(f"box.space['test']:replace{{'{name}', {case['tarantool']}}}") self.assertSequenceEqual(self.con.select('test', name), [[name, case['python']]]) def test_msgpack_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.assertEqual(packer_default(case['python']), msgpack.ExtType(code=2, data=case['msgpack'])) @skip_or_run_uuid_test def test_tarantool_encode(self): - for name in self.cases.keys(): + for name, case in self.cases.items(): with self.subTest(msg=name): - case = self.cases[name] - self.con.insert('test', [name, case['python']]) lua_eval = f""" From f831312c83f9d2e48bba0c77f1013c83cb1b2ae3 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 13:34:17 +0300 Subject: [PATCH 029/190] pylint: fix consider-using-enumerate cases Fix all cases of C0200 consider-using-enumerate. Part of #270 --- tarantool/msgpack_ext/decimal.py | 3 +-- test/suites/test_pool.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index 3ffd59b7..3e333cc7 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -244,8 +244,7 @@ def encode(obj, _): bytes_reverted = bytearray() scale = 0 - for i in range(len(str_repr)): - str_digit = str_repr[i] + for i, str_digit in enumerate(str_repr): if str_digit == '.': scale = len(str_repr) - i - 1 break diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index b5b59dfa..10031792 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -57,8 +57,8 @@ def set_ro(self, srv, read_only): def set_cluster_ro(self, read_only_list): assert len(self.servers) == len(read_only_list) - for i in range(len(self.servers)): - self.set_ro(self.servers[i], read_only_list[i]) + for i, server in enumerate(self.servers): + self.set_ro(server, read_only_list[i]) def retry(self, func, count=5, timeout=0.5): for i in range(count): From 55a22d5b145c8eefaf10a855d4d4c3f5d203bb38 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 13:50:20 +0300 Subject: [PATCH 030/190] pylint: fix consider-iterating-dictionary cases Fix all cases of C0201 consider-iterating-dictionary. Part of #270 --- test/suites/test_schema.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 4201d579..001fe2cb 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -470,7 +470,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): else: self.assertEqual(con.schema_version, 0) self.assertEqual(con.schema, None) - for method_case in self.testing_methods['unavailable'].keys(): + for method_case in self.testing_methods['unavailable']: with self.subTest(name=method_case): if isinstance(con, tarantool.ConnectionPool) and method_case == 'space': continue @@ -488,7 +488,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): '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(): + for method_case in self.testing_methods['available']: with self.subTest(name=method_case): testing_function = getattr(con, method_case) if mode is not None: @@ -514,7 +514,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): # Testing the schemaful connection with methods # that should NOW be available. - for method_case in self.testing_methods['unavailable'].keys(): + for method_case in self.testing_methods['unavailable']: with self.subTest(name=method_case): if isinstance(con, tarantool.ConnectionPool) and method_case == 'space': continue @@ -534,7 +534,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): 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(): + for method_case in self.testing_methods['available']: with self.subTest(name=method_case): testing_function = getattr(con, method_case) if mode is not None: From e79187df69560d92828a7759e5a1704dd4e98eaa Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:01:15 +0300 Subject: [PATCH 031/190] pylint: fix bad-classmethod-argument cases Fix all cases of C0202 bad-classmethod-argument. Part of #270 --- test/suites/test_connection.py | 24 +++++----- test/suites/test_crud.py | 14 +++--- test/suites/test_datetime.py | 24 +++++----- test/suites/test_dbapi.py | 26 +++++------ test/suites/test_decimal.py | 24 +++++----- test/suites/test_dml.py | 52 ++++++++++----------- test/suites/test_encoding.py | 38 ++++++++-------- test/suites/test_error_ext.py | 38 ++++++++-------- test/suites/test_execute.py | 18 ++++---- test/suites/test_interval.py | 24 +++++----- test/suites/test_mesh.py | 2 +- test/suites/test_package.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_protocol.py | 20 ++++----- test/suites/test_push.py | 14 +++--- test/suites/test_reconnect.py | 10 ++--- test/suites/test_schema.py | 82 +++++++++++++++++----------------- test/suites/test_ssl.py | 40 ++++++++--------- test/suites/test_uuid.py | 24 +++++----- 19 files changed, 239 insertions(+), 239 deletions(-) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 6492d77e..cab0211f 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -12,15 +12,15 @@ class TestSuiteConnection(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' CONNECTION '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" box.schema.user.create('test', {password = 'test', if_not_exists = true}) box.schema.user.grant('test', 'read,write,execute', 'universe') @@ -151,11 +151,11 @@ def my_unpacker_factory(_): self.assertIsInstance(resp[0], tuple) @classmethod - def tearDown(self): - if hasattr(self, 'con'): - self.con.close() + def tearDown(cls): + if hasattr(cls, 'con'): + cls.con.close() @classmethod - def tearDownClass(self): - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 9ac56b33..b2681d55 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -21,13 +21,13 @@ def create_server(): class TestSuiteCrud(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' CRUD '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) # Create server and extract helpful fields for tests. - self.srv = create_server() - self.host = self.srv.host - self.port = self.srv.args['primary'] + cls.srv = create_server() + cls.host = cls.srv.host + cls.port = cls.srv.args['primary'] def setUp(self): time.sleep(1) @@ -749,7 +749,7 @@ def tearDown(self): self.conn_pool.close() @classmethod - def tearDownClass(self): + def tearDownClass(cls): # Stop instance. - self.srv.stop() - self.srv.clean() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 5062c3f9..45ad1183 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -15,15 +15,15 @@ class TestSuiteDatetime(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' DATETIME EXT TYPE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" _, datetime = pcall(require, 'datetime') box.schema.space.create('test') @@ -54,8 +54,8 @@ def setUpClass(self): rawset(_G, 'sub', sub) """) - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - user='test', password='test') + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + user='test', password='test') def setUp(self): # prevent a remote tarantool from clean our session @@ -529,7 +529,7 @@ def test_primary_key(self): self.assertSequenceEqual(self.con.select('test_pk', data[0]), [data]) @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 638c79ff..7bea5311 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -19,17 +19,17 @@ class TestSuiteDBAPI(dbapi20.DatabaseAPI20Test): 'drink varchar(30))' @classmethod - def setUpClass(self): + def setUpClass(cls): print(' DBAPI '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary']) - self.driver = dbapi - self.connect_kw_args = dict( - host=self.srv.host, - port=self.srv.args['primary']) + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) + cls.driver = dbapi + cls.connect_kw_args = dict( + host=cls.srv.host, + port=cls.srv.args['primary']) @skip_or_run_sql_test def setUp(self): @@ -43,10 +43,10 @@ def setUp(self): "execute', 'universe')") @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() def test_rowcount(self): con = self._connect() diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 42ae8746..233610e5 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -14,15 +14,15 @@ class TestSuiteDecimal(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' DECIMAL EXT TYPE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" _, decimal = pcall(require, 'decimal') box.schema.space.create('test') @@ -43,8 +43,8 @@ def setUpClass(self): box.schema.user.grant('test', 'read,write,execute', 'universe') """) - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - user='test', password='test') + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + user='test', password='test') def setUp(self): # prevent a remote tarantool from clean our session @@ -422,7 +422,7 @@ def test_primary_key(self): @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 4b3461bd..eeee2df6 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -8,44 +8,44 @@ class TestSuiteRequest(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' DML '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary']) - self.adm = self.srv.admin - self.space_created = self.adm("box.schema.create_space('space_1')") - self.adm(""" + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) + cls.adm = cls.srv.admin + cls.space_created = cls.adm("box.schema.create_space('space_1')") + cls.adm(""" box.space['space_1']:create_index('primary', { type = 'tree', parts = {1, 'num'}, unique = true}) """.replace('\n', ' ')) - self.adm(""" + cls.adm(""" box.space['space_1']:create_index('secondary', { type = 'tree', parts = {2, 'num', 3, 'str'}, unique = false}) """.replace('\n', ' ')) - self.space_created = self.adm("box.schema.create_space('space_2')") - self.adm(""" + cls.space_created = cls.adm("box.schema.create_space('space_2')") + cls.adm(""" box.space['space_2']:create_index('primary', { type = 'hash', parts = {1, 'num'}, unique = true}) """.replace('\n', ' ')) - self.adm("json = require('json')") - self.adm("fiber = require('fiber')") - self.adm("uuid = require('uuid')") + cls.adm("json = require('json')") + cls.adm("fiber = require('fiber')") + cls.adm("uuid = require('uuid')") if not sys.platform.startswith("win"): - self.sock_srv = TarantoolServer(create_unix_socket=True) - self.sock_srv.script = 'test/suites/box.lua' - self.sock_srv.start() + cls.sock_srv = TarantoolServer(create_unix_socket=True) + cls.sock_srv.script = 'test/suites/box.lua' + cls.sock_srv.start() else: - self.sock_srv = None + cls.sock_srv = None def setUp(self): # prevent a remote tarantool from clean our session @@ -399,11 +399,11 @@ def test_16_extra_error_info_fields(self): self.fail('Expected error') @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() - - if self.sock_srv is not None: - self.sock_srv.stop() - self.sock_srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() + + if cls.sock_srv is not None: + cls.sock_srv.stop() + cls.sock_srv.clean() diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 9394515f..858ce978 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -11,34 +11,34 @@ class TestSuiteEncoding(unittest.TestCase): # pylint: disable=invalid-name @classmethod - def setUpClass(self): + def setUpClass(cls): print(' ENCODING '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.srv.admin(""" + cls.srv.admin(""" box.schema.user.create('test', { password = 'test' }) box.schema.user.grant('test', 'execute,read,write', 'universe') """) - args = [self.srv.host, self.srv.args['primary']] + args = [cls.srv.host, cls.srv.args['primary']] kwargs = { 'user': 'test', 'password': 'test' } - self.con_encoding_utf8 = tarantool.Connection(*args, encoding='utf-8', **kwargs) - self.con_encoding_none = tarantool.Connection(*args, encoding=None, **kwargs) - self.conns = [self.con_encoding_utf8, self.con_encoding_none] + cls.con_encoding_utf8 = tarantool.Connection(*args, encoding='utf-8', **kwargs) + cls.con_encoding_none = tarantool.Connection(*args, encoding=None, **kwargs) + cls.conns = [cls.con_encoding_utf8, cls.con_encoding_none] - self.srv.admin("box.schema.create_space('space_str')") - self.srv.admin(""" + cls.srv.admin("box.schema.create_space('space_str')") + cls.srv.admin(""" box.space['space_str']:create_index('primary', { type = 'tree', parts = {1, 'str'}, unique = true}) """.replace('\n', ' ')) - self.srv.admin("box.schema.create_space('space_varbin')") - self.srv.admin(r""" + cls.srv.admin("box.schema.create_space('space_varbin')") + cls.srv.admin(r""" box.space['space_varbin']:format({ { 'id', @@ -52,13 +52,13 @@ def setUpClass(self): } }) """.replace('\n', ' ')) - self.srv.admin(""" + cls.srv.admin(""" box.space['space_varbin']:create_index('id', { type = 'tree', parts = {1, 'number'}, unique = true}) """.replace('\n', ' ')) - self.srv.admin(""" + cls.srv.admin(""" box.space['space_varbin']:create_index('varbin', { type = 'tree', parts = {2, 'varbinary'}, @@ -197,8 +197,8 @@ def test_02_05_error_extra_info_decode_for_encoding_none_behavior(self): self.fail('Expected error') @classmethod - def tearDownClass(self): - for con in self.conns: + def tearDownClass(cls): + for con in cls.conns: con.close() - self.srv.stop() - self.srv.clean() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index c77cb421..78b16593 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -14,15 +14,15 @@ class TestSuiteErrorExt(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' ERROR EXT TYPE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" box.schema.space.create('test') box.space['test']:create_index('primary', { type = 'tree', @@ -35,23 +35,23 @@ def setUpClass(self): box.schema.user.create('no_grants', {if_not_exists = true}) """) - self.conn_encoding_utf8 = tarantool.Connection( - self.srv.host, self.srv.args['primary'], + cls.conn_encoding_utf8 = tarantool.Connection( + cls.srv.host, cls.srv.args['primary'], user='test', password='test', encoding='utf-8') - self.conn_encoding_none = tarantool.Connection( - self.srv.host, self.srv.args['primary'], + cls.conn_encoding_none = tarantool.Connection( + cls.srv.host, cls.srv.args['primary'], user='test', password='test', encoding=None) - if self.adm.tnt_version >= pkg_resources.parse_version('2.10.0'): - self.conn_encoding_utf8.eval(r""" + if cls.adm.tnt_version >= pkg_resources.parse_version('2.10.0'): + cls.conn_encoding_utf8.eval(r""" local err = box.error.new(box.error.UNKNOWN) rawset(_G, 'simple_error', err) """) # https://github.com/tarantool/tarantool/blob/125c13c81abb302708771ba04d59382d44a4a512/test/box-tap/extended_error.test.lua - self.conn_encoding_utf8.eval(r""" + cls.conn_encoding_utf8.eval(r""" local user = box.session.user() box.schema.func.create('forbidden_function', {body = 'function() end'}) box.session.su('no_grants') @@ -61,7 +61,7 @@ def setUpClass(self): """) # https://github.com/tarantool/tarantool/blob/125c13c81abb302708771ba04d59382d44a4a512/test/box-tap/extended_error.test.lua - self.conn_encoding_utf8.eval(r""" + cls.conn_encoding_utf8.eval(r""" local e1 = box.error.new(box.error.UNKNOWN) local e2 = box.error.new(box.error.UNKNOWN) e2:set_prev(e1) @@ -386,8 +386,8 @@ def test_tarantool_encode(self): @classmethod - def tearDownClass(self): - self.conn_encoding_utf8.close() - self.conn_encoding_none.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.conn_encoding_utf8.close() + cls.conn_encoding_none.close() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 40c5029e..93a80d4f 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -19,13 +19,13 @@ class TestSuiteExecute(unittest.TestCase): ] @classmethod - def setUpClass(self): + def setUpClass(cls): print(' EXECUTE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary']) + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) @skip_or_run_sql_test def setUp(self): @@ -39,10 +39,10 @@ def setUp(self): "execute', 'universe')") @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() def _populate_data(self, table_name): query = f"insert into {table_name} values (:id, :name)" diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 5affbd76..9bbe7252 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -16,15 +16,15 @@ class TestSuiteInterval(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' INTERVAL EXT TYPE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" _, datetime = pcall(require, 'datetime') box.schema.space.create('test') @@ -47,8 +47,8 @@ def setUpClass(self): rawset(_G, 'sub', sub) """) - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - user='test', password='test') + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + user='test', password='test') def setUp(self): # prevent a remote tarantool from clean our session @@ -289,7 +289,7 @@ def test_tarantool_interval_subtraction(self): @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index d3a8a402..cc08492a 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -49,7 +49,7 @@ def define_custom_cluster_function(self, func_name, retval): srv.admin(func_body) @classmethod - def setUpClass(self): + def setUpClass(cls): print(' MESH '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 7b40a150..648ef1d4 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -20,7 +20,7 @@ def is_test_pure_install(): class TestSuitePackage(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' PACKAGE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 10031792..739a7f7c 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -71,7 +71,7 @@ def retry(self, func, count=5, timeout=0.5): time.sleep(timeout) @classmethod - def setUpClass(self): + def setUpClass(cls): print(' POOL '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 4b6b2de2..0b5afcdf 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -17,14 +17,14 @@ class TestSuiteProtocol(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' PROTOCOL '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary']) - self.adm = self.srv.admin + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) + cls.adm = cls.srv.admin def setUp(self): # prevent a remote tarantool from clean our session @@ -87,8 +87,8 @@ def test_04_protocol(self): self.assertEqual(self.con._features[IPROTO_FEATURE_WATCHERS], False) @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 18e71a80..0c9c9c4b 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -77,13 +77,13 @@ def push_callback(data, on_push_ctx=[]): class TestSuitePush(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' PUSH '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) # Create server and extract helpful fields for tests. - self.srv = create_server() - self.host = self.srv.host - self.port = self.srv.args['primary'] + cls.srv = create_server() + cls.host = cls.srv.host + cls.port = cls.srv.args['primary'] def setUp(self): # Open connection, connection pool and mesh connection to instance. @@ -248,7 +248,7 @@ def tearDown(self): self.mesh_conn.close() @classmethod - def tearDownClass(self): + def tearDownClass(cls): # Stop instance. - self.srv.stop() - self.srv.clean() + cls.srv.stop() + cls.srv.clean() diff --git a/test/suites/test_reconnect.py b/test/suites/test_reconnect.py index 61616e46..04bf8bd2 100644 --- a/test/suites/test_reconnect.py +++ b/test/suites/test_reconnect.py @@ -7,11 +7,11 @@ class TestSuiteReconnect(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' RECONNECT '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' def setUp(self): # prevent a remote tarantool from clean our session @@ -80,5 +80,5 @@ def test_03_connect_after_close(self): self.srv.stop() @classmethod - def tearDownClass(self): - self.srv.clean() + def tearDownClass(cls): + cls.srv.clean() diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 001fe2cb..f294c41b 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -38,20 +38,20 @@ class TestSuiteSchemaAbstract(unittest.TestCase): # Define 'encoding' field in a concrete class. @classmethod - def setUpClass(self): - params = f'connection.encoding: {repr(self.encoding)}' + def setUpClass(cls): + params = f'connection.encoding: {repr(cls.encoding)}' print(f' SCHEMA ({params}) '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - self.srv.admin("box.schema.user.create('test', {password = 'test', " + + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.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')") + cls.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(""" + cls.srv.admin("function server_function() return 2+2 end") + cls.srv.admin(""" box.schema.create_space( 'tester', { format = { @@ -60,7 +60,7 @@ def setUpClass(self): } }) """) - self.srv.admin(""" + cls.srv.admin(""" box.space.tester:create_index( 'primary_index', { parts = { @@ -68,36 +68,36 @@ def setUpClass(self): } }) """) - self.srv.admin("box.space.tester:insert({1, null})") + cls.srv.admin("box.space.tester:insert({1, null})") - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - 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') + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + encoding=cls.encoding, user='test', password='test') + cls.con_schema_disable = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + encoding=cls.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 + cls.mesh_con_schema_disable = tarantool.MeshConnection(host=cls.srv.host, + port=cls.srv.args['primary'], + fetch_schema=False, + user='test', password='test') + cls.pool_con_schema_disable = tarantool.ConnectionPool([{'host':cls.srv.host, + 'port':cls.srv.args['primary']}], + user='test', password='test', + fetch_schema=False) + cls.sch = cls.con.schema # The relevant test cases mainly target Python 2, where # a user may want to pass a string literal as a space or # an index name and don't bother whether all symbols in it # are ASCII. - self.unicode_space_name_literal = '∞' - self.unicode_index_name_literal = '→' + cls.unicode_space_name_literal = '∞' + cls.unicode_index_name_literal = '→' - self.unicode_space_name_u = u'∞' - self.unicode_index_name_u = u'→' - self.unicode_space_id, self.unicode_index_id = self.srv.admin(""" + cls.unicode_space_name_u = u'∞' + cls.unicode_index_name_u = u'→' + cls.unicode_space_id, cls.unicode_index_id = cls.srv.admin(""" do local space = box.schema.create_space('\\xe2\\x88\\x9e') local index = space:create_index('\\xe2\\x86\\x92') @@ -105,8 +105,8 @@ def setUpClass(self): end """) - if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): - self.srv.admin(""" + if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): + cls.srv.admin(""" box.schema.create_space( 'constr_tester_1', { format = { @@ -588,24 +588,24 @@ def test_11_foreign_key_invalid_replace(self): self.con.replace('constr_tester_2', [2, 999, 623]) @classmethod - def tearDownClass(self): + def tearDownClass(cls): # We need to drop spaces with foreign keys with predetermined order, # otherwise remote server clean() will fail to clean up resources. - if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): - self.srv.admin(""" + if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): + cls.srv.admin(""" box.space.constr_tester_2:drop() box.space.constr_tester_1:drop() """) - self.con.close() - self.con_schema_disable.close() + cls.con.close() + cls.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() + cls.mesh_con_schema_disable.close() + cls.pool_con_schema_disable.close() + cls.srv.stop() + cls.srv.clean() class TestSuiteSchemaUnicodeConnection(TestSuiteSchemaAbstract): diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index fda0f7c8..1f434084 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -72,34 +72,34 @@ def __init__(self, @unittest.skipIf(not is_test_ssl(), "TEST_TNT_SSL is not set.") class TestSuiteSsl(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' SSL '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) test_suites_dir = os.path.dirname(__file__) test_data_dir = os.path.join(test_suites_dir, "..", "data") - self.cert_file = os.path.join(test_data_dir, "localhost.crt") - self.invalidhost_cert_file = os.path.join(test_data_dir, + cls.cert_file = os.path.join(test_data_dir, "localhost.crt") + cls.invalidhost_cert_file = os.path.join(test_data_dir, "invalidhost.crt") - self.key_file = os.path.join(test_data_dir, "localhost.key") - self.key_enc_file = os.path.join(test_data_dir, "localhost.enc.key") - self.ca_file = os.path.join(test_data_dir, "ca.crt") - self.empty_file = os.path.join(test_data_dir, "empty") - self.password = "mysslpassword" - self.invalid_password = "notmysslpassword" - self.password_file = os.path.join(test_data_dir, "passwords") - self.invalid_password_file = os.path.join(test_data_dir, "invalidpasswords") - self.invalid_file = "any_invalid_path" + cls.key_file = os.path.join(test_data_dir, "localhost.key") + cls.key_enc_file = os.path.join(test_data_dir, "localhost.enc.key") + cls.ca_file = os.path.join(test_data_dir, "ca.crt") + cls.empty_file = os.path.join(test_data_dir, "empty") + cls.password = "mysslpassword" + cls.invalid_password = "notmysslpassword" + cls.password_file = os.path.join(test_data_dir, "passwords") + cls.invalid_password_file = os.path.join(test_data_dir, "invalidpasswords") + cls.invalid_file = "any_invalid_path" # Extract the version for skips. - self.tnt_version = None - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() - fetch_tarantool_version(self) - self.srv.stop() - self.srv.clean() - self.srv = None + cls.tnt_version = None + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + fetch_tarantool_version(cls) + cls.srv.stop() + cls.srv.clean() + cls.srv = None def stop_srv(self, srv): if srv: diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 299c1161..3c7cc04a 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -14,15 +14,15 @@ class TestSuiteUUID(unittest.TestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): print(' UUID EXT TYPE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - self.srv = TarantoolServer() - self.srv.script = 'test/suites/box.lua' - self.srv.start() + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() - self.adm = self.srv.admin - self.adm(r""" + cls.adm = cls.srv.admin + cls.adm(r""" _, uuid = pcall(require, 'uuid') box.schema.space.create('test') @@ -43,8 +43,8 @@ def setUpClass(self): box.schema.user.grant('test', 'read,write,execute', 'universe') """) - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], - user='test', password='test') + cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], + user='test', password='test') def setUp(self): # prevent a remote tarantool from clean our session @@ -134,7 +134,7 @@ def test_primary_key(self): @classmethod - def tearDownClass(self): - self.con.close() - self.srv.stop() - self.srv.clean() + def tearDownClass(cls): + cls.con.close() + cls.srv.stop() + cls.srv.clean() From b223f71900db6e87c6baceda4bac6242e17956cb Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:09:40 +0300 Subject: [PATCH 032/190] pylint: fix superfluous-parens cases Fix all cases of C0325 superfluous-parens. Part of #270 --- tarantool/msgpack_ext/decimal.py | 2 +- test/suites/lib/remote_tarantool_server.py | 4 ++-- test/suites/test_dml.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index 3e333cc7..04e14c7f 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -326,7 +326,7 @@ def add_str_digit(digit, digits_reverted, scale): :meta private: """ - if not (0 <= digit <= 9): + if not 0 <= digit <= 9: raise MsgpackError('Unexpected MP_DECIMAL digit nibble') if len(digits_reverted) == scale: diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index f1049c60..cb90b4dd 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -26,7 +26,7 @@ def __init__(self): self.args['primary'] = BINARY_PORT self.args['admin'] = os.environ['REMOTE_TARANTOOL_CONSOLE_PORT'] - assert(self.args['primary'] != self.args['admin']) + assert self.args['primary'] != self.args['admin'] # a name to using for a lock self.whoami = get_random_string() @@ -54,7 +54,7 @@ def acquire_lock(self): self.lock_is_acquired = True def touch_lock(self): - assert(self.lock_is_acquired) + assert self.lock_is_acquired res = self.admin.execute(f'return touch_lock("{self.whoami}")') ok = res[0] err = res[1] if not ok else None diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index eeee2df6..912278a1 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -111,7 +111,7 @@ def test_02_select(self): select_req = self.con.select('space_1', [0], index='secondary') self.assertEqual(len(select_req), 99) for i in select_req: - self.assertTrue(not (i[0] % 5)) + self.assertTrue(not i[0] % 5) self.assertTrue(not i[1]) self.assertTrue(i[2] == 'tuple_' + str(i[0])) From 6672bd400bc7976239d75144fdbed259e2d2b180 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:10:26 +0300 Subject: [PATCH 033/190] pylint: fix multiple-statements cases Fix all cases of C0321 multiple-statements. Part of #270 --- test/suites/lib/tarantool_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index d67f6243..d9520077 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -72,7 +72,8 @@ def script_dst(self): @property def script(self): - if not hasattr(self, '_script'): self._script = None + if not hasattr(self, '_script'): + self._script = None return self._script @script.setter From a7141090b1aa1815655ad8ad8ecdd237935775a7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:11:41 +0300 Subject: [PATCH 034/190] pylint: fix trailing-newlines cases Fix all cases of C0305 trailing-newlines. Part of #270 --- test/suites/test_protocol.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 0b5afcdf..3794b85e 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -91,4 +91,3 @@ def tearDownClass(cls): cls.con.close() cls.srv.stop() cls.srv.clean() - From b0cf56f28b6bc5a1826a716b431858347959a1a4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:14:48 +0300 Subject: [PATCH 035/190] pylint: disable too-many-lines cases Disable all current cases of C0302 too-many-lines. Part of #270 --- tarantool/connection.py | 1 + tarantool/connection_pool.py | 1 + tarantool/msgpack_ext/types/timezones/gen-timezones.sh | 1 + tarantool/msgpack_ext/types/timezones/timezones.py | 1 + 4 files changed, 4 insertions(+) diff --git a/tarantool/connection.py b/tarantool/connection.py index 86476a33..2bbc139d 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1,6 +1,7 @@ """ This module provides API for interaction with a Tarantool server. """ +# pylint: disable=too-many-lines import os import time diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index dd49d0f6..ec23a9f7 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -1,6 +1,7 @@ """ This module provides API for interaction with Tarantool servers cluster. """ +# pylint: disable=too-many-lines import abc import itertools diff --git a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh index 5de5a51a..d87868d9 100755 --- a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh +++ b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh @@ -26,6 +26,7 @@ cat < ${DST_FILE} Tarantool timezone info. Automatically generated by \`\`gen-timezones.sh\`\`. """ +# pylint: disable=too-many-lines TZ_UTC = 0x01 TZ_RFC = 0x02 diff --git a/tarantool/msgpack_ext/types/timezones/timezones.py b/tarantool/msgpack_ext/types/timezones/timezones.py index 0e3ff770..98d63535 100644 --- a/tarantool/msgpack_ext/types/timezones/timezones.py +++ b/tarantool/msgpack_ext/types/timezones/timezones.py @@ -2,6 +2,7 @@ Tarantool timezone info. Automatically generated by ``gen-timezones.sh``. """ +# pylint: disable=too-many-lines TZ_UTC = 0x01 TZ_RFC = 0x02 From a47eb617bed2f56da78cb1160914cb19176f00b9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:24:39 +0300 Subject: [PATCH 036/190] pylint: fix wrong-import-order cases Fix all cases of C0411 wrong-import-order. Part of #270 --- tarantool/connection.py | 2 +- tarantool/request.py | 3 +-- test/setup_command.py | 4 ++-- test/suites/lib/skip.py | 3 ++- test/suites/lib/tarantool_admin.py | 3 ++- test/suites/lib/tarantool_server.py | 3 +-- test/suites/test_crud.py | 3 ++- test/suites/test_datetime.py | 7 ++++--- test/suites/test_decimal.py | 7 ++++--- test/suites/test_error_ext.py | 5 +++-- test/suites/test_interval.py | 7 ++++--- test/suites/test_protocol.py | 10 +++++----- test/suites/test_schema.py | 5 +++-- test/suites/test_uuid.py | 7 ++++--- 14 files changed, 38 insertions(+), 31 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 2bbc139d..ba729a3d 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -18,6 +18,7 @@ import ctypes import ctypes.util from ctypes import c_ssize_t +from typing import Union import msgpack @@ -108,7 +109,6 @@ CrudError, call_crud, ) -from typing import Union WWSAEWOULDBLOCK = 10035 ER_UNKNOWN_REQUEST_TYPE = 48 diff --git a/tarantool/request.py b/tarantool/request.py index 2b7d672f..9cb0e055 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -4,11 +4,10 @@ """ import sys -import msgpack import hashlib - from collections.abc import Sequence, Mapping +import msgpack from tarantool.error import DatabaseError from tarantool.const import ( diff --git a/test/setup_command.py b/test/setup_command.py index fe250743..5fc1239c 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -3,11 +3,11 @@ import os import sys import unittest -import setuptools from distutils.errors import DistutilsError - from glob import glob +import setuptools + class Test(setuptools.Command): user_options = [] description = 'Run tests' diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 4b464caa..6bf64724 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -1,8 +1,9 @@ import functools -import pkg_resources import re import sys +import pkg_resources + def fetch_tarantool_version(self): """Helper to fetch current Tarantool version. """ diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index 77bb2528..c30b633c 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -1,7 +1,8 @@ import socket -import yaml import re + import pkg_resources +import yaml class TarantoolAdmin(object): diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index d9520077..0d82f214 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -10,12 +10,11 @@ import shutil import subprocess -from .tarantool_admin import TarantoolAdmin - from tarantool.const import ( SSL_TRANSPORT ) +from .tarantool_admin import TarantoolAdmin def check_port(port, rais=True): try: diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index b2681d55..004b49b7 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -2,10 +2,11 @@ import sys import time import unittest + import tarantool -from .lib.tarantool_server import TarantoolServer from tarantool.error import DatabaseError +from .lib.tarantool_server import TarantoolServer def create_server(): srv = TarantoolServer() diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 45ad1183..68565482 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,17 +1,18 @@ import sys import re import unittest -import msgpack import warnings -import tarantool + +import msgpack import pandas +import tarantool +from tarantool.error import MsgpackError, MsgpackWarning from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test -from tarantool.error import MsgpackError, MsgpackWarning class TestSuiteDatetime(unittest.TestCase): @classmethod diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 233610e5..a5f8c63f 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -1,16 +1,17 @@ import sys import unittest import decimal -import msgpack import warnings -import tarantool +import msgpack + +import tarantool +from tarantool.error import MsgpackError, MsgpackWarning from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_decimal_test -from tarantool.error import MsgpackError, MsgpackWarning class TestSuiteDecimal(unittest.TestCase): @classmethod diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 78b16593..5a86244e 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,11 +1,12 @@ import sys import unittest import uuid -import msgpack import warnings -import tarantool import pkg_resources +import msgpack + +import tarantool from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 9bbe7252..e1e55490 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,18 +1,19 @@ import re import sys import unittest -import msgpack import warnings -import tarantool + +import msgpack import pandas import pytz +import tarantool +from tarantool.error import MsgpackError from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test -from tarantool.error import MsgpackError class TestSuiteInterval(unittest.TestCase): @classmethod diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 3794b85e..9c77d31d 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,19 +1,19 @@ import sys -import pkg_resources import unittest import uuid -import tarantool -from tarantool.utils import greeting_decode, version_id - -from .lib.tarantool_server import TarantoolServer +import pkg_resources +import tarantool from tarantool.const import ( IPROTO_FEATURE_STREAMS, IPROTO_FEATURE_TRANSACTIONS, IPROTO_FEATURE_ERROR_EXTENSION, IPROTO_FEATURE_WATCHERS, ) +from tarantool.utils import greeting_decode, version_id + +from .lib.tarantool_server import TarantoolServer class TestSuiteProtocol(unittest.TestCase): @classmethod diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index f294c41b..2f7c3939 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,11 +1,12 @@ import sys import unittest -import tarantool import pkg_resources +import tarantool +from tarantool.error import NotSupportedError + from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_constraints_test -from tarantool.error import NotSupportedError # FIXME: I'm quite sure that there is a simpler way to count diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 3c7cc04a..b50344de 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -1,16 +1,17 @@ import sys import unittest import uuid -import msgpack import warnings -import tarantool +import msgpack + +import tarantool +from tarantool.error import MsgpackError, MsgpackWarning from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_uuid_test -from tarantool.error import MsgpackError, MsgpackWarning class TestSuiteUUID(unittest.TestCase): @classmethod From 18b929c81818d74d0621e2006a729f2e65e052fc Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:27:25 +0300 Subject: [PATCH 037/190] pylint: fix wrong-import-position cases Fix all cases of C0413 wrong-import-position. Part of #270 --- docs/source/conf.py | 3 +-- tarantool/utils.py | 3 +-- test/suites/__init__.py | 5 ++--- test/suites/test_package.py | 4 ++-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 44c24173..f6294722 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,6 +11,7 @@ # pylint: disable=invalid-name import sys, os +import tarantool # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -53,8 +54,6 @@ # |version| and |release|, also used in various other places throughout the # built documents. -import tarantool - # The short X.Y version. version = tarantool.__version__ # The full version, including alpha/beta/rc tags. diff --git a/tarantool/utils.py b/tarantool/utils.py index b0f0614d..bd2b724e 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -1,10 +1,9 @@ +from base64 import decodebytes as base64_decode import sys import uuid ENCODING_DEFAULT = "utf-8" -from base64 import decodebytes as base64_decode - def strxor(rhs, lhs): """ XOR two strings. diff --git a/test/suites/__init__.py b/test/suites/__init__.py index 2768e143..b473da88 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -1,9 +1,6 @@ import os import unittest -__tmp = os.getcwd() -os.chdir(os.path.abspath(os.path.dirname(__file__))) - from .test_schema import TestSuiteSchemaUnicodeConnection from .test_schema import TestSuiteSchemaBinaryConnection from .test_dml import TestSuiteRequest @@ -40,6 +37,8 @@ def load_tests(loader, tests, pattern): suite.addTests(loader.loadTestsFromTestCase(testc)) return suite +__tmp = os.getcwd() +os.chdir(os.path.abspath(os.path.dirname(__file__))) os.chdir(__tmp) diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 648ef1d4..03b4ff04 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -2,13 +2,13 @@ import sys import unittest +import tarantool + if sys.version_info >= (3, 8): from importlib import metadata else: import importlib_metadata as metadata -import tarantool - def is_test_pure_install(): env = os.getenv("TEST_PURE_INSTALL") From 5da722689f9054f3ab39f1f3386f4dcc9be0bfa2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:30:14 +0300 Subject: [PATCH 038/190] pylint: fix import-outside-toplevel case Fix the only case of C0415 import-outside-toplevel. Part of #270 --- test/suites/lib/tarantool_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 0d82f214..c9caa118 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -15,6 +15,7 @@ ) from .tarantool_admin import TarantoolAdmin +from .remote_tarantool_server import RemoteTarantoolServer def check_port(port, rais=True): try: @@ -130,7 +131,6 @@ def __new__(cls, create_unix_socket=False, auth_type=None): if os.name == 'nt': - from .remote_tarantool_server import RemoteTarantoolServer return RemoteTarantoolServer() return super(TarantoolServer, cls).__new__(cls) From b0b87b8c22d2d3ae114cfbf0e2a59528486a78fd Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:30:45 +0300 Subject: [PATCH 039/190] pylint: ignore setup dependency import issues Disable a case of C0415 import-outside-toplevel and possible E0401 import-error: we allow to run commands like `python setup.py test` without setuptools_scm. Part of #270 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index ece0e140..7d73bb1d 100755 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ class BuildPyCommand(build_py): def run(self): # Import here to allow to run commands # like `python setup.py test` without setuptools_scm. + # pylint: disable=import-outside-toplevel,import-error from setuptools_scm import get_version version = get_version() From 3e86a2e603873a0bb30b20ceeeb0488cc1dae1cd Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 14:57:43 +0300 Subject: [PATCH 040/190] pylint: fix missing-module-docstring cases Fix all cases of C0114 missing-module-docstring. Part of #270 --- docs/source/conf.py | 22 ++++++++++++---------- setup.py | 3 +++ tarantool/__init__.py | 4 ++++ tarantool/const.py | 4 ++++ tarantool/utils.py | 4 ++++ test/setup_command.py | 3 +++ test/suites/__init__.py | 4 ++++ test/suites/lib/remote_tarantool_server.py | 5 +++++ test/suites/lib/skip.py | 4 ++++ test/suites/lib/tarantool_admin.py | 4 ++++ test/suites/lib/tarantool_server.py | 4 ++++ test/suites/test_connection.py | 4 ++++ test/suites/test_crud.py | 4 ++++ test/suites/test_datetime.py | 4 ++++ test/suites/test_dbapi.py | 4 ++++ test/suites/test_decimal.py | 4 ++++ test/suites/test_dml.py | 4 ++++ test/suites/test_encoding.py | 4 ++++ test/suites/test_error_ext.py | 4 ++++ test/suites/test_execute.py | 4 ++++ test/suites/test_interval.py | 4 ++++ test/suites/test_mesh.py | 5 +++++ test/suites/test_package.py | 4 ++++ test/suites/test_pool.py | 5 +++++ test/suites/test_protocol.py | 4 ++++ test/suites/test_push.py | 4 ++++ test/suites/test_reconnect.py | 4 ++++ test/suites/test_schema.py | 4 ++++ test/suites/test_ssl.py | 4 ++++ test/suites/test_uuid.py | 4 ++++ 30 files changed, 129 insertions(+), 10 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f6294722..308c14be 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,13 +1,15 @@ -# Tarantool python client library documentation build configuration file, created by -# sphinx-quickstart on Tue Nov 29 06:29:57 2011. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +""" +Tarantool python client library documentation build configuration file, created by +sphinx-quickstart on Tue Nov 29 06:29:57 2011. + +This file is execfile()d with the current directory set to its containing dir. + +Note that not all possible configuration values are present in this +autogenerated file. + +All configuration values have a default; values that are commented out +serve to show the default. +""" # pylint: disable=invalid-name import sys, os diff --git a/setup.py b/setup.py index 7d73bb1d..f60cc89e 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" +Package setup commands. +""" import codecs import os diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 071032c1..499b1055 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -1,3 +1,7 @@ +""" +This package provides API for interaction with a Tarantool server. +""" + import sys from tarantool.connection import Connection diff --git a/tarantool/const.py b/tarantool/const.py index edb34608..194e97a9 100644 --- a/tarantool/const.py +++ b/tarantool/const.py @@ -1,3 +1,7 @@ +""" +This module a set of constants for the package. +""" + IPROTO_REQUEST_TYPE = 0x00 IPROTO_SYNC = 0x01 # replication keys (header) diff --git a/tarantool/utils.py b/tarantool/utils.py index bd2b724e..6c48e5ac 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -1,3 +1,7 @@ +""" +This module provides untility functions for the package. +""" + from base64 import decodebytes as base64_decode import sys import uuid diff --git a/test/setup_command.py b/test/setup_command.py index 5fc1239c..c4a9da35 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +""" +This module describes class implementing `python setup.py test`. +""" import os import sys diff --git a/test/suites/__init__.py b/test/suites/__init__.py index b473da88..520cf4e6 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -1,3 +1,7 @@ +""" +Module tests entrypoint. +""" + import os import unittest diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index cb90b4dd..643361c2 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -1,3 +1,8 @@ +""" +This module provides helpers to work with remote Tarantool server +(used on Windows). +""" + import sys import os import random diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 6bf64724..2b3a871a 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -1,3 +1,7 @@ +""" +This module provides helpers to skip specific tests. +""" + import functools import re import sys diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index c30b633c..ac4c33d4 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -1,3 +1,7 @@ +""" +This module provides helpers to setup running Tarantool server. +""" + import socket import re diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index c9caa118..5d9ff4ff 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -1,3 +1,7 @@ +""" +This module provides helpers start up a Tarantool server. +""" + import os import os.path import errno diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index cab0211f..006f7807 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -1,3 +1,7 @@ +""" +This module tests basic connection behavior. +""" + import sys import unittest diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 004b49b7..d048507b 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -1,3 +1,7 @@ +""" +This module tests integration with tarantool/crud. +""" + import re import sys import time diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 68565482..88f367c3 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,3 +1,7 @@ +""" +This module tests work with datetime type. +""" + import sys import re import unittest diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 7bea5311..3899b497 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -1,3 +1,7 @@ +""" +This module tests compatibility with DBAPI standards. +""" + import sys import unittest diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index a5f8c63f..009afc68 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -1,3 +1,7 @@ +""" +This module tests work with decimal type. +""" + import sys import unittest import decimal diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 912278a1..065a5baa 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,3 +1,7 @@ +""" +This module tests basic data operations. +""" + import sys import unittest import tarantool diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 858ce978..27691c2c 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -1,3 +1,7 @@ +""" +This module tests various type encoding cases. +""" + import sys import unittest diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 5a86244e..99e62175 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,3 +1,7 @@ +""" +This module tests work with extended error type. +""" + import sys import unittest import uuid diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 93a80d4f..91d45162 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -1,3 +1,7 @@ +""" +This module tests API fo execute SQL on a server. +""" + import sys import unittest diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index e1e55490..8de50539 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,3 +1,7 @@ +""" +This module tests work with datetime interval type. +""" + import re import sys import unittest diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index cc08492a..5b850523 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -1,3 +1,8 @@ +""" +This module tests work with a cluster of Tarantool servers through +MeshConnection. +""" + import sys import unittest import warnings diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 03b4ff04..82cf1856 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -1,3 +1,7 @@ +""" +This module tests package features. +""" + import os import sys import unittest diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 739a7f7c..c0bb1cf1 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -1,3 +1,8 @@ +""" +This module tests work with a cluster of Tarantool servers through +ConnectionPool. +""" + import sys import time import unittest diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 9c77d31d..7c5436b9 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,3 +1,7 @@ +""" +This module tests connection authentication. +""" + import sys import unittest import uuid diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 0c9c9c4b..036f38ab 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -1,3 +1,7 @@ +""" +This module tests work with IPROTO pushed messages. +""" + import sys import unittest import tarantool diff --git a/test/suites/test_reconnect.py b/test/suites/test_reconnect.py index 04bf8bd2..ec2a9b21 100644 --- a/test/suites/test_reconnect.py +++ b/test/suites/test_reconnect.py @@ -1,3 +1,7 @@ +""" +This module tests basic reconnect behavior. +""" + import sys import unittest import warnings diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 2f7c3939..898fd165 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,3 +1,7 @@ +""" +This module tests space and index schema fetch. +""" + import sys import unittest import pkg_resources diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 1f434084..06aa2f52 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,3 +1,7 @@ +""" +This module tests connection through SSL. +""" + import os import sys import unittest diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index b50344de..2e86fb11 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -1,3 +1,7 @@ +""" +This module tests work with UUID type. +""" + import sys import unittest import uuid From c816695b76c320cd128d232ec0b21d4619a78c2d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 15:09:21 +0300 Subject: [PATCH 041/190] internal: rework protocol greeting Use Dataclass for protocol Greeting, as it should be. Extract the definition outside the function. Part of #270 --- tarantool/utils.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/tarantool/utils.py b/tarantool/utils.py index 6c48e5ac..d09adb1e 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -3,6 +3,8 @@ """ from base64 import decodebytes as base64_decode +from dataclasses import dataclass, field +import typing import sys import uuid @@ -68,6 +70,32 @@ def version_id(major, minor, patch): return (((major << 8) | minor) << 8) | patch +@dataclass +class Greeting(): + """ + Connection greeting info. + """ + + version_id: typing.Optional = 0 + """ + :type: :obj:`tuple` or :obj:`list` + """ + + protocol: typing.Optional[str] = None + """ + :type: :obj:`str`, optional + """ + + uuid: typing.Optional[str] = None + """ + :type: :obj:`str`, optional + """ + + salt: typing.Optional[str] = None + """ + :type: :obj:`str`, optional + """ + def greeting_decode(greeting_buf): """ Decode Tarantool server greeting. @@ -81,12 +109,6 @@ def greeting_decode(greeting_buf): :raise: :exc:`~Exception` """ - class Greeting: - version_id = 0 - protocol = None - uuid = None - salt = None - # Tarantool 1.6.6 # Tarantool 1.6.6-102-g4e9bde2 # Tarantool 1.6.8 (Binary) 3b151c25-4c4a-4b5d-8042-0f1b3a6f61c3 From 0508a8c7c8f1531faea78acb2c14aa70b3fa6fce Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 15:26:53 +0300 Subject: [PATCH 042/190] internal: rework join stages Use Enum for preplication JoinState, as it should be. Extract the definition outside the function. 1. https://www.tarantool.io/en/doc/latest/dev_guide/internals/iproto/replication/#box-protocol-join Part of #270 --- tarantool/connection.py | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index ba729a3d..20174a1e 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -6,6 +6,7 @@ import os import time import errno +from enum import Enum import socket try: import ssl @@ -454,6 +455,33 @@ def crud_unflatten_rows(self, rows, metadata): raise NotImplementedError +class JoinState(Enum): + """ + Current replication join state. See `join protocol`_ for more info. + + .. _join protocol: https://www.tarantool.io/en/doc/latest/dev_guide/internals/iproto/replication/#box-protocol-join + """ + + HANDSHAKE = 1 + """ + Sent the join request. + """ + + INITIAL = 2 + """ + Received inital vclock. + """ + + FINAL = 3 + """ + Received current vclock. + """ + + DONE = 4 + """ + No more messages expected. + """ + class Connection(ConnectionInterface): """ Represents a connection to the Tarantool server. @@ -1521,20 +1549,21 @@ def _join_v17(self, server_uuid): :exc:`~tarantool.error.SslError` """ - class JoinState: - Handshake, Initial, Final, Done = range(4) - request = RequestJoin(self, server_uuid) self._socket.sendall(bytes(request)) - state = JoinState.Handshake + state = JoinState.HANDSHAKE while True: resp = Response(self, self._read_response()) yield resp if resp.code >= REQUEST_TYPE_ERROR: return elif resp.code == REQUEST_TYPE_OK: - state = state + 1 - if state == JoinState.Done: + if state == JoinState.HANDSHAKE: + state = JoinState.INITIAL + elif state == JoinState.INITIAL: + state = JoinState.FINAL + elif state == JoinState.FINAL: + state = JoinState.DONE return def _ops_process(self, space, update_ops): From 8e50707a137f770ab129f10340f9e26f68ca9cd2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 15:57:13 +0300 Subject: [PATCH 043/190] pylint: fix missing-class-docstring cases Fix several cases of C0115 missing-class-docstring. Part of #270 --- setup.py | 9 ++++++--- test/setup_command.py | 4 ++++ test/suites/lib/remote_tarantool_server.py | 4 ++++ test/suites/lib/tarantool_admin.py | 4 ++++ test/suites/lib/tarantool_server.py | 8 ++++---- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index f60cc89e..311f6330 100755 --- a/setup.py +++ b/setup.py @@ -18,10 +18,13 @@ cmdclass = {} command_options = {} -# Build the package -# python setup.py build_py -# builds the package with generating correspondent VERSION file class BuildPyCommand(build_py): + """ + Build the package + python setup.py build_py + builds the package with generating correspondent VERSION file + """ + def run(self): # Import here to allow to run commands # like `python setup.py test` without setuptools_scm. diff --git a/test/setup_command.py b/test/setup_command.py index c4a9da35..19fb8a05 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -12,6 +12,10 @@ import setuptools class Test(setuptools.Command): + """ + Class implementing `python setup.py test`. + """ + user_options = [] description = 'Run tests' diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index 643361c2..7cf7f99b 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -24,6 +24,10 @@ def get_random_string(): class RemoteTarantoolServer(object): + """ + Class to work with remote Tarantool server. + """ + def __init__(self): self.host = os.environ['REMOTE_TARANTOOL_HOST'] diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index ac4c33d4..56c7485f 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -10,6 +10,10 @@ class TarantoolAdmin(object): + """ + Class to setup running Tarantool server. + """ + def __init__(self, host, port): self.host = host self.port = port diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 5d9ff4ff..5ffc3071 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -43,11 +43,11 @@ def find_port(port=None): return find_port(3300) -class RunnerException(object): - pass - - class TarantoolServer(object): + """ + Class to start up a new Tarantool server. + """ + default_tarantool = { "bin": "tarantool", "logfile": "tarantool.log", From a355369971d5508b3cfd4c4a26fb6877a3e4f5a9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 15:57:44 +0300 Subject: [PATCH 044/190] pylint: fix missing-function-docstring cases Fix several cases of C0116 missing-function-docstring. Part of #270 --- setup.py | 19 ++-- tarantool/dbapi.py | 5 + test/setup_command.py | 12 ++- test/suites/__init__.py | 4 + test/suites/lib/remote_tarantool_server.py | 32 ++++++ test/suites/lib/skip.py | 48 ++++++--- test/suites/lib/tarantool_admin.py | 20 ++++ test/suites/lib/tarantool_server.py | 115 ++++++++++++++++++--- 8 files changed, 218 insertions(+), 37 deletions(-) diff --git a/setup.py b/setup.py index 311f6330..fb7340f9 100755 --- a/setup.py +++ b/setup.py @@ -26,6 +26,10 @@ class BuildPyCommand(build_py): """ def run(self): + """ + Run the command. + """ + # Import here to allow to run commands # like `python setup.py test` without setuptools_scm. # pylint: disable=import-outside-toplevel,import-error @@ -61,11 +65,19 @@ def run(self): def read(*parts): + """ + Read the file. + """ + filename = os.path.join(os.path.dirname(__file__), *parts) with codecs.open(filename, encoding='utf-8') as file: return file.read() def get_dependencies(filename): + """ + Get package dependencies from the `requirements.txt`. + """ + root = os.path.dirname(os.path.realpath(__file__)) requirements = os.path.join(root, filename) result = [] @@ -74,13 +86,6 @@ def get_dependencies(filename): return file.read().splitlines() raise RuntimeError("Unable to get dependencies from file " + filename) -def find_version(*file_paths): - version_file = read(*file_paths) - version_match = re.search(r"""^__version__\s*=\s*(['"])(.+)\1""", - version_file, re.M) - if version_match: - return version_match.group(2) - raise RuntimeError("Unable to find version string.") packages = [item for item in find_packages('.') if item.startswith('tarantool')] diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 88e29d32..ec145d58 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -50,6 +50,11 @@ def callproc(self, procname, *params): @property def rows(self): + """ + Returns the current count of rows in cursor. + + :type: :obj:`int` + """ return self._rows @property diff --git a/test/setup_command.py b/test/setup_command.py index 19fb8a05..58e1b14e 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -20,15 +20,23 @@ class Test(setuptools.Command): description = 'Run tests' def initialize_options(self): + """ + Do nothing. setuptools requires to override this abstract + method. + """ pass def finalize_options(self): + """ + Do nothing. setuptools requires to override this abstract + method. + """ pass def run(self): - ''' + """ Find all tests in test/tarantool/ and run them - ''' + """ tests = unittest.defaultTestLoader.discover('test', pattern='suites') test_runner = unittest.TextTestRunner(verbosity=2) diff --git a/test/suites/__init__.py b/test/suites/__init__.py index 520cf4e6..f3ec7b95 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -36,6 +36,10 @@ TestSuiteConnection, TestSuiteCrud,) def load_tests(loader, tests, pattern): + """ + Add suites to test run. + """ + suite = unittest.TestSuite() for testc in test_cases: suite.addTests(loader.loadTestsFromTestCase(testc)) diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index 7cf7f99b..5c5ab350 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -20,6 +20,9 @@ def get_random_string(): + """ + :type: :obj:`str` + """ return ''.join(random.choice(string.ascii_lowercase) for _ in range(16)) @@ -48,6 +51,10 @@ def __init__(self): self.admin.execute('box.cfg{listen = box.NULL}') def acquire_lock(self): + """ + Acquire lock on the remote server so no concurrent tests would run. + """ + deadline = time.time() + AWAIT_TIME while True: res = self.admin.execute(f'return acquire_lock("{ self.whoami}")') @@ -63,6 +70,11 @@ def acquire_lock(self): self.lock_is_acquired = True def touch_lock(self): + """ + Refresh locked state on the remote server so no concurrent + tests would run. + """ + assert self.lock_is_acquired res = self.admin.execute(f'return touch_lock("{self.whoami}")') ok = res[0] @@ -71,6 +83,11 @@ def touch_lock(self): raise RuntimeError(f'can not update "{self.whoami}" lock: {str(err)}') def release_lock(self): + """ + Release loack so another test suite can run on the remote + server. + """ + res = self.admin.execute(f'return release_lock("{self.whoami}")') ok = res[0] err = res[1] if not ok else None @@ -79,18 +96,33 @@ def release_lock(self): self.lock_is_acquired = False def start(self): + """ + Initialize the work with the remote server. + """ + if not self.lock_is_acquired: self.acquire_lock() self.admin.execute(f'box.cfg{{listen = "0.0.0.0:{self.args["primary"]}"}}') def stop(self): + """ + Finish the work with the remote server. + """ + self.admin.execute('box.cfg{listen = box.NULL}') self.release_lock() def is_started(self): + """ + Check if we still work with the remote server. + """ + return self.lock_is_acquired def clean(self): + """ + Do nothing. + """ pass def __del__(self): diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 2b3a871a..c39d0151 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -9,7 +9,8 @@ import pkg_resources def fetch_tarantool_version(self): - """Helper to fetch current Tarantool version. + """ + Helper to fetch current Tarantool version. """ if not hasattr(self, 'tnt_version') or self.tnt_version is None: srv = None @@ -28,7 +29,8 @@ def fetch_tarantool_version(self): self.__class__.tnt_version = srv.admin.tnt_version def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): - """Helper to skip or run tests depending on the Tarantool + """ + Helper to skip or run tests depending on the Tarantool version. Also, it can be used with the 'setUp' method for skipping @@ -43,7 +45,8 @@ def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): def skip_or_run_test_tarantool(func, required_tt_version, msg): - """Decorator to skip or run tests depending on the tarantool + """ + Decorator to skip or run tests depending on the tarantool version. Also, it can be used with the 'setUp' method for skipping @@ -63,7 +66,8 @@ def wrapper(self, *args, **kwargs): return wrapper def skip_or_run_test_tarantool_call(self, required_tt_version, msg): - """Function to skip or run tests depending on the tarantool + """ + Function to skip or run tests depending on the tarantool version. Useful in cases when in is inconvenient to work with decorators. @@ -75,7 +79,8 @@ def skip_or_run_test_tarantool_call(self, required_tt_version, msg): def skip_or_run_test_pcall_require(func, required_tt_module, msg): - """Decorator to skip or run tests depending on tarantool + """ + Decorator to skip or run tests depending on tarantool module requre success or fail. Also, it can be used with the 'setUp' method for skipping @@ -108,7 +113,8 @@ def wrapper(self, *args, **kwargs): def skip_or_run_test_python(func, required_python_version, msg): - """Decorator to skip or run tests depending on the Python version. + """ + Decorator to skip or run tests depending on the Python version. Also, it can be used with the 'setUp' method for skipping the whole test suite. @@ -133,7 +139,8 @@ def wrapper(self, *args, **kwargs): def skip_or_run_sql_test(func): - """Decorator to skip or run SQL-related tests depending on the + """ + Decorator to skip or run SQL-related tests depending on the tarantool version. Tarantool supports SQL-related stuff only since 2.0.0 version. @@ -145,7 +152,8 @@ def skip_or_run_sql_test(func): def skip_or_run_varbinary_test(func): - """Decorator to skip or run VARBINARY-related tests depending on + """ + Decorator to skip or run VARBINARY-related tests depending on the tarantool version. Tarantool supports VARBINARY type only since 2.2.1 version. @@ -157,7 +165,8 @@ def skip_or_run_varbinary_test(func): def skip_or_run_decimal_test(func): - """Decorator to skip or run decimal-related tests depending on + """ + Decorator to skip or run decimal-related tests depending on the tarantool version. Tarantool supports decimal type only since 2.2.1 version. @@ -168,7 +177,8 @@ def skip_or_run_decimal_test(func): 'does not support decimal type') def skip_or_run_uuid_test(func): - """Decorator to skip or run UUID-related tests depending on + """ + Decorator to skip or run UUID-related tests depending on the tarantool version. Tarantool supports UUID type only since 2.4.1 version. @@ -179,7 +189,8 @@ def skip_or_run_uuid_test(func): 'does not support UUID type') def skip_or_run_datetime_test(func): - """Decorator to skip or run datetime-related tests depending on + """ + Decorator to skip or run datetime-related tests depending on the tarantool version. Tarantool supports datetime type only since 2.10.0 version. @@ -190,7 +201,8 @@ def skip_or_run_datetime_test(func): 'does not support datetime type') def skip_or_run_error_extra_info_test(func): - """Decorator to skip or run tests related to extra error info + """ + Decorator to skip or run tests related to extra error info provided over iproto depending on the tarantool version. Tarantool provides extra error info only since 2.4.1 version. @@ -201,7 +213,8 @@ def skip_or_run_error_extra_info_test(func): 'does not provide extra error info') def skip_or_run_error_ext_type_test(func): - """Decorator to skip or run tests related to error extension + """ + Decorator to skip or run tests related to error extension type depending on the tarantool version. Tarantool supports error extension type only since 2.4.1 version, @@ -214,7 +227,8 @@ def skip_or_run_error_ext_type_test(func): 'does not support error extension type') def skip_or_run_ssl_password_test_call(self): - """Function to skip or run tests related to SSL password + """ + Function to skip or run tests related to SSL password and SSL password files support. Supported only in Tarantool EE. Do not check Enterprise prefix since TNT_SSL_TEST already assumes it. @@ -228,7 +242,8 @@ def skip_or_run_ssl_password_test_call(self): 'does not support SSL passwords') def skip_or_run_auth_type_test_call(self): - """Function to skip or run tests related to configuring + """ + Function to skip or run tests related to configuring authentication method. Tarantool supports auth_type only in current master since @@ -243,7 +258,8 @@ def skip_or_run_auth_type_test_call(self): 'does not support auth type') def skip_or_run_constraints_test(func): - """Decorator to skip or run tests related to spaces with + """ + Decorator to skip or run tests related to spaces with schema constraints. Tarantool supports schema constraints only since 2.10.0 version. diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index 56c7485f..cf57ea73 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -22,18 +22,30 @@ def __init__(self, host, port): self._tnt_version = None def connect(self): + """ + Connect to running Tarantool server. + """ + self.socket = socket.create_connection((self.host, self.port)) self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) self.is_connected = True self.socket.recv(256) # skip greeting def disconnect(self): + """ + Disconnect from the Tarantool server. + """ + if self.is_connected: self.socket.close() self.socket = None self.is_connected = False def reconnect(self): + """ + Reconnect to the running Tarantool server. + """ + self.disconnect() self.connect() @@ -48,6 +60,10 @@ def __call__(self, command): return self.execute(command) def execute(self, command): + """ + Evaluate some Lua code on the Tarantool server. + """ + if not command: return @@ -77,6 +93,10 @@ def execute(self, command): @property def tnt_version(self): + """ + Connected Tarantool server version. + """ + if self._tnt_version is not None: return self._tnt_version diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 5ffc3071..95c720e0 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -22,6 +22,10 @@ from .remote_tarantool_server import RemoteTarantoolServer def check_port(port, rais=True): + """ + Check if port is free. + """ + try: sock = socket.create_connection(("0.0.0.0", port)) except socket.error: @@ -34,6 +38,10 @@ def check_port(port, rais=True): def find_port(port=None): + """ + Pick some free socket. + """ + if port is None: port = random.randrange(3300, 9999) while port < 9999: @@ -60,28 +68,52 @@ class TarantoolServer(object): @property def logfile_path(self): + """ + Path to server logs. + """ + return os.path.join(self.vardir, self.default_tarantool['logfile']) @property def cfgfile_path(self): + """ + Path to server configuration. + """ + return os.path.join(self.vardir, self.default_tarantool['config']) @property def script_path(self): + """ + Path to server init.lua script. + """ + return os.path.join(self.vardir, self.default_tarantool['init']) @property def script_dst(self): + """ + Path to server init.lua folder. + """ + return os.path.join(self.vardir, os.path.basename(self.script)) @property def script(self): + """ + Get server init.lua script. + """ + if not hasattr(self, '_script'): self._script = None return self._script @script.setter def script(self, val): + """ + Set server init.lua script. + """ + if val is None: if hasattr(self, '_script'): delattr(self, '_script') @@ -90,18 +122,30 @@ def script(self, val): @property def binary(self): + """ + Get Tarantool binary used to start the server. + """ + if not hasattr(self, '_binary'): self._binary = self.find_exe() return self._binary @property def _admin(self): + """ + Get admin connection used to set up the server. + """ + if not hasattr(self, 'admin'): self.admin = None return self.admin @_admin.setter def _admin(self, port): + """ + Set admin connection used to set up the server. + """ + try: int(port) except ValueError: @@ -112,12 +156,20 @@ def _admin(self, port): @property def log_des(self): + """ + Get server log file descriptor. + """ + if not hasattr(self, '_log_des'): self._log_des = open(self.logfile_path, 'a') return self._log_des @log_des.deleter def log_des(self): + """ + Set server log file descriptor. + """ + if not hasattr(self, '_log_des'): return if not self._log_des.closed: @@ -177,6 +229,10 @@ def __init__(self, self.auth_type = auth_type def find_exe(self): + """ + Find Tarantool executable. + """ + if 'TARANTOOL_BOX_PATH' in os.environ: os.environ["PATH"] = os.environ["TARANTOOL_BOX_PATH"] + os.pathsep + os.environ["PATH"] @@ -187,6 +243,10 @@ def find_exe(self): raise RuntimeError("Can't find server executable in " + os.environ["PATH"]) def generate_listen(self, port, port_only): + """ + Generate Tarantool server box.cfg listen. + """ + if not port_only and self.transport == SSL_TRANSPORT: listen = self.host + ":" + str(port) + "?transport=ssl&" if self.ssl_key_file: @@ -207,6 +267,10 @@ def generate_listen(self, port, port_only): return listen def generate_configuration(self): + """ + Generate Tarantool box.cfg values. + """ + primary_listen = self.generate_listen(self.args['primary'], False) admin_listen = self.generate_listen(self.args['admin'], True) os.putenv("LISTEN", primary_listen) @@ -217,10 +281,15 @@ def generate_configuration(self): os.putenv("AUTH_TYPE", "") def prepare_args(self): + """ + Prepare Tarantool server init.lua script. + """ + return shlex.split(self.binary if not self.script else self.script_dst) def wait_until_started(self): - """ Wait until server is started. + """ + Wait until server is started. Server consists of two parts: 1) wait until server is listening on sockets @@ -246,15 +315,18 @@ def wait_until_started(self): raise def start(self): - # Main steps for running Tarantool\Box - # * Find binary file --DONE(find_exe -> binary) - # * Create vardir --DONE(__init__) - # * Generate cfgfile --DONE(generate_configuration) - # * (MAYBE) Copy init.lua --INSIDE - # * Concatenate arguments and - # start Tarantool\Box --DONE(prepare_args) - # * Wait unitl Tarantool\Box - # started --DONE(wait_until_started) + """ + Main steps for running Tarantool\\Box + * Find binary file --DONE(find_exe -> binary) + * Create vardir --DONE(__init__) + * Generate cfgfile --DONE(generate_configuration) + * (MAYBE) Copy init.lua --INSIDE + * Concatenate arguments and + start Tarantool\\Box --DONE(prepare_args) + * Wait unitl Tarantool\\Box + started --DONE(wait_until_started) + """ + self.generate_configuration() if self.script: shutil.copy(self.script, self.script_dst) @@ -267,16 +339,28 @@ def start(self): self.wait_until_started() def stop(self): + """ + Stop Tarantool server. + """ + self.admin.disconnect() if self.process.poll() is None: self.process.terminate() self.process.wait() def restart(self): + """ + Restart Tarantool server. + """ + self.stop() self.start() def clean(self): + """ + Clean Tarantool resources. + """ + if os.path.isdir(self.vardir): shutil.rmtree(self.vardir) @@ -293,9 +377,16 @@ def __del__(self): self.clean() def touch_lock(self): - # A stub method to be compatible with - # RemoteTarantoolServer. + """ + A stub method to be compatible with + RemoteTarantoolServer. + """ + pass def is_started(self): + """ + Is Tarantool server has need started. + """ + return self.process is not None From 1f6a91973719e08782e9e2f2db0743b6c5dcc773 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:29:42 +0300 Subject: [PATCH 045/190] pylint: disable missing-class-docstring for tests Disable remaining cases of C0115 missing-class-docstring for unittest classes. Part of #270 --- test/suites/test_connection.py | 1 + test/suites/test_crud.py | 1 + test/suites/test_datetime.py | 1 + test/suites/test_dbapi.py | 1 + test/suites/test_decimal.py | 1 + test/suites/test_dml.py | 1 + test/suites/test_encoding.py | 1 + test/suites/test_error_ext.py | 1 + test/suites/test_execute.py | 1 + test/suites/test_interval.py | 1 + test/suites/test_mesh.py | 1 + test/suites/test_package.py | 1 + test/suites/test_pool.py | 1 + test/suites/test_protocol.py | 1 + test/suites/test_push.py | 1 + test/suites/test_reconnect.py | 1 + test/suites/test_schema.py | 1 + test/suites/test_ssl.py | 1 + test/suites/test_uuid.py | 1 + 19 files changed, 19 insertions(+) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 006f7807..ec65d280 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -1,6 +1,7 @@ """ This module tests basic connection behavior. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index d048507b..bfb5ac79 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -1,6 +1,7 @@ """ This module tests integration with tarantool/crud. """ +# pylint: disable=missing-class-docstring import re import sys diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 88f367c3..db733310 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,6 +1,7 @@ """ This module tests work with datetime type. """ +# pylint: disable=missing-class-docstring import sys import re diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 3899b497..a73d8d3a 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -1,6 +1,7 @@ """ This module tests compatibility with DBAPI standards. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 009afc68..b7e8ba3d 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -1,6 +1,7 @@ """ This module tests work with decimal type. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 065a5baa..8b687ed9 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,6 +1,7 @@ """ This module tests basic data operations. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 27691c2c..60240b10 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -1,6 +1,7 @@ """ This module tests various type encoding cases. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 99e62175..2b3c5219 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,6 +1,7 @@ """ This module tests work with extended error type. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 91d45162..a1a5bc12 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -1,6 +1,7 @@ """ This module tests API fo execute SQL on a server. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 8de50539..9aa9dfea 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,6 +1,7 @@ """ This module tests work with datetime interval type. """ +# pylint: disable=missing-class-docstring import re import sys diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index 5b850523..e7160f40 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -2,6 +2,7 @@ This module tests work with a cluster of Tarantool servers through MeshConnection. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 82cf1856..1bbe8f39 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -1,6 +1,7 @@ """ This module tests package features. """ +# pylint: disable=missing-class-docstring import os import sys diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index c0bb1cf1..021e6765 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,6 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ +# pylint: disable=missing-class-docstring import sys import time diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 7c5436b9..fe1f43e1 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,6 +1,7 @@ """ This module tests connection authentication. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 036f38ab..7302191e 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -1,6 +1,7 @@ """ This module tests work with IPROTO pushed messages. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_reconnect.py b/test/suites/test_reconnect.py index ec2a9b21..1dfac4e7 100644 --- a/test/suites/test_reconnect.py +++ b/test/suites/test_reconnect.py @@ -1,6 +1,7 @@ """ This module tests basic reconnect behavior. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 898fd165..734a3140 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,6 +1,7 @@ """ This module tests space and index schema fetch. """ +# pylint: disable=missing-class-docstring import sys import unittest diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 06aa2f52..77475e41 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,6 +1,7 @@ """ This module tests connection through SSL. """ +# pylint: disable=missing-class-docstring import os import sys diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 2e86fb11..4bb116f1 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -1,6 +1,7 @@ """ This module tests work with UUID type. """ +# pylint: disable=missing-class-docstring import sys import unittest From 038d00ed93d6ec3f2be9013f73c289d9fef739c4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:31:14 +0300 Subject: [PATCH 046/190] pylint: disable missing-function-docstring for tests Disable remaining cases of C0116 missing-function-docstring for unittest classes. Part of #270 --- test/suites/test_connection.py | 2 +- test/suites/test_crud.py | 2 +- test/suites/test_datetime.py | 2 +- test/suites/test_dbapi.py | 2 +- test/suites/test_decimal.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_encoding.py | 2 +- test/suites/test_error_ext.py | 2 +- test/suites/test_execute.py | 2 +- test/suites/test_interval.py | 2 +- test/suites/test_mesh.py | 2 +- test/suites/test_package.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_protocol.py | 2 +- test/suites/test_push.py | 2 +- test/suites/test_reconnect.py | 2 +- test/suites/test_schema.py | 2 +- test/suites/test_ssl.py | 2 +- test/suites/test_uuid.py | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index ec65d280..77814a0b 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -1,7 +1,7 @@ """ This module tests basic connection behavior. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index bfb5ac79..a57e8929 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -1,7 +1,7 @@ """ This module tests integration with tarantool/crud. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import re import sys diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index db733310..eb17e092 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,7 +1,7 @@ """ This module tests work with datetime type. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import re diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index a73d8d3a..c001a42c 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -1,7 +1,7 @@ """ This module tests compatibility with DBAPI standards. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index b7e8ba3d..6b3b8fb8 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -1,7 +1,7 @@ """ This module tests work with decimal type. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 8b687ed9..4d1bad6e 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,7 +1,7 @@ """ This module tests basic data operations. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 60240b10..d2e0568b 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -1,7 +1,7 @@ """ This module tests various type encoding cases. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 2b3c5219..41b664de 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,7 +1,7 @@ """ This module tests work with extended error type. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index a1a5bc12..2d6d0478 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -1,7 +1,7 @@ """ This module tests API fo execute SQL on a server. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 9aa9dfea..64fdbc85 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,7 +1,7 @@ """ This module tests work with datetime interval type. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import re import sys diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index e7160f40..c5cdc49d 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through MeshConnection. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 1bbe8f39..24be800f 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -1,7 +1,7 @@ """ This module tests package features. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import os import sys diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 021e6765..801ebe44 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import time diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index fe1f43e1..c966c210 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,7 +1,7 @@ """ This module tests connection authentication. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 7302191e..a3d19c45 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -1,7 +1,7 @@ """ This module tests work with IPROTO pushed messages. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_reconnect.py b/test/suites/test_reconnect.py index 1dfac4e7..f7811fab 100644 --- a/test/suites/test_reconnect.py +++ b/test/suites/test_reconnect.py @@ -1,7 +1,7 @@ """ This module tests basic reconnect behavior. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 734a3140..c9a4bbc0 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 77475e41..b984126f 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,7 +1,7 @@ """ This module tests connection through SSL. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import os import sys diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 4bb116f1..b136b1fe 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -1,7 +1,7 @@ """ This module tests work with UUID type. """ -# pylint: disable=missing-class-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring import sys import unittest From 5733a87eaf737404ee1013c3bd3d1070af8c1a28 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:33:23 +0300 Subject: [PATCH 047/190] schema: make internals consistent Before this patch, _key_tuple would be not a tuple if space is provided and index is not. Since select allows both scalars and tuples, it wasn't a bug, but still seems confusing. Part of #270 --- tarantool/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index 2ed1a0ea..e38e9ec1 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -404,7 +404,7 @@ def fetch_index_from(self, space, index): if space is None and index is None: _key_tuple = () elif space is not None and index is None: - _key_tuple = (space) + _key_tuple = (space, ) elif space is not None and index is not None: _key_tuple = (space, index) else: From d742c60bdabec4f0269f2931c777279c701c0785 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:50:51 +0300 Subject: [PATCH 048/190] pylint: fix dangerous-default-value cases Fix all cases of W0102 dangerous-default-value. Part of #270 --- tarantool/connection.py | 148 ++++++++++++++++++++++++----------- tarantool/connection_pool.py | 44 +++++------ test/suites/test_push.py | 2 +- 3 files changed, 124 insertions(+), 70 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 20174a1e..72b44056 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -19,7 +19,7 @@ import ctypes import ctypes.util from ctypes import c_ssize_t -from typing import Union +from typing import Optional, Union import msgpack @@ -263,7 +263,7 @@ def execute(self, query, params): raise NotImplementedError @abc.abstractmethod - def crud_insert(self, space_name, values, opts={}): + def crud_insert(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_insert`. """ @@ -271,7 +271,7 @@ def crud_insert(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_insert_object(self, space_name, values, opts={}): + def crud_insert_object(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_insert_object`. """ @@ -279,7 +279,7 @@ def crud_insert_object(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_insert_many(self, space_name, values, opts={}): + def crud_insert_many(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_insert_many`. """ @@ -287,7 +287,7 @@ def crud_insert_many(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_insert_object_many(self, space_name, values, opts={}): + def crud_insert_object_many(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_insert_object_many`. """ @@ -295,7 +295,7 @@ def crud_insert_object_many(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_get(self, space_name, key, opts={}): + def crud_get(self, space_name, key, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_get`. """ @@ -303,7 +303,7 @@ def crud_get(self, space_name, key, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_update(self, space_name, key, operations=[], opts={}): + def crud_update(self, space_name, key, operations=None, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_update`. """ @@ -311,7 +311,7 @@ def crud_update(self, space_name, key, operations=[], opts={}): raise NotImplementedError @abc.abstractmethod - def crud_delete(self, space_name, key, opts={}): + def crud_delete(self, space_name, key, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_delete`. """ @@ -319,7 +319,7 @@ def crud_delete(self, space_name, key, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_replace(self, space_name, values, opts={}): + def crud_replace(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_replace`. """ @@ -327,7 +327,7 @@ def crud_replace(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_replace_object(self, space_name, values, opts={}): + def crud_replace_object(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_replace_object`. """ @@ -335,7 +335,7 @@ def crud_replace_object(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_replace_many(self, space_name, values, opts={}): + def crud_replace_many(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_replace_many`. """ @@ -343,7 +343,7 @@ def crud_replace_many(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_replace_object_many(self, space_name, values, opts={}): + def crud_replace_object_many(self, space_name, values, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_replace_object_many`. """ @@ -351,7 +351,7 @@ def crud_replace_object_many(self, space_name, values, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_upsert(self, space_name, values, operations=[], opts={}): + def crud_upsert(self, space_name, values, operations=None, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_upsert`. """ @@ -359,7 +359,7 @@ def crud_upsert(self, space_name, values, operations=[], opts={}): raise NotImplementedError @abc.abstractmethod - def crud_upsert_object(self, space_name, values, operations=[], opts={}): + def crud_upsert_object(self, space_name, values, operations=None, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_upsert_object`. """ @@ -367,7 +367,7 @@ def crud_upsert_object(self, space_name, values, operations=[], opts={}): raise NotImplementedError @abc.abstractmethod - def crud_upsert_many(self, space_name, values_operation, opts={}): + def crud_upsert_many(self, space_name, values_operation, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_upsert_many`. """ @@ -375,7 +375,7 @@ def crud_upsert_many(self, space_name, values_operation, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_upsert_object_many(self, space_name, values_operation, opts={}): + def crud_upsert_object_many(self, space_name, values_operation, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_upsert_object_many`. """ @@ -383,7 +383,7 @@ def crud_upsert_object_many(self, space_name, values_operation, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_select(self, space_name: str, conditions=[], opts={}): + def crud_select(self, space_name: str, conditions=None, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_select`. """ @@ -391,7 +391,7 @@ def crud_select(self, space_name: str, conditions=[], opts={}): raise NotImplementedError @abc.abstractmethod - def crud_min(self, space_name, index_name, opts={}): + def crud_min(self, space_name, index_name, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_min`. """ @@ -399,7 +399,7 @@ def crud_min(self, space_name, index_name, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_max(self, space_name, index_name, opts={}): + def crud_max(self, space_name, index_name, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_max`. """ @@ -407,7 +407,7 @@ def crud_max(self, space_name, index_name, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_truncate(self, space_name, opts={}): + def crud_truncate(self, space_name, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_truncate`. """ @@ -415,7 +415,7 @@ def crud_truncate(self, space_name, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_len(self, space_name, opts={}): + def crud_len(self, space_name, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_len`. """ @@ -423,7 +423,7 @@ def crud_len(self, space_name, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_storage_info(self, opts={}): + def crud_storage_info(self, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_storage_info`. """ @@ -431,7 +431,7 @@ def crud_storage_info(self, opts={}): raise NotImplementedError @abc.abstractmethod - def crud_count(self, space_name, conditions=[], opts={}): + def crud_count(self, space_name, conditions=None, opts=None): """ Reference implementation: :meth:`~tarantool.Connection.crud_count`. """ @@ -2195,7 +2195,7 @@ def _packer_factory(self): def _unpacker_factory(self): return self._unpacker_factory_impl(self) - def crud_insert(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_insert(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Inserts row through the `crud `__. @@ -2217,6 +2217,8 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], opts: dict={} assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.insert", space_name, values, opts) @@ -2226,7 +2228,7 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], opts: dict={} return CrudResult(crud_resp[0]) - def crud_insert_object(self, space_name: str, values: dict, opts: dict={}) -> CrudResult: + def crud_insert_object(self, space_name: str, values: dict, opts: Optional[dict]=None) -> CrudResult: """ Inserts object row through the `crud `__. @@ -2248,6 +2250,8 @@ def crud_insert_object(self, space_name: str, values: dict, opts: dict={}) -> Cr assert isinstance(space_name, str) assert isinstance(values, dict) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.insert_object", space_name, values, opts) @@ -2257,7 +2261,7 @@ def crud_insert_object(self, space_name: str, values: dict, opts: dict={}) -> Cr return CrudResult(crud_resp[0]) - def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Inserts batch rows through the `crud `__. @@ -2279,6 +2283,8 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: di assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.insert_many", space_name, values, opts) @@ -2295,7 +2301,7 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: di return res - def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Inserts batch object rows through the `crud `__. @@ -2317,6 +2323,8 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], o assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.insert_object_many", space_name, values, opts) @@ -2333,7 +2341,7 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], o return res - def crud_get(self, space_name: str, key: int, opts: dict={}) -> CrudResult: + def crud_get(self, space_name: str, key: int, opts: Optional[dict]=None) -> CrudResult: """ Gets row through the `crud `__. @@ -2354,6 +2362,8 @@ def crud_get(self, space_name: str, key: int, opts: dict={}) -> CrudResult: """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.get", space_name, key, opts) @@ -2363,7 +2373,7 @@ def crud_get(self, space_name: str, key: int, opts: dict={}) -> CrudResult: return CrudResult(crud_resp[0]) - def crud_update(self, space_name: str, key: int, operations: list=[], opts: dict={}) -> CrudResult: + def crud_update(self, space_name: str, key: int, operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: """ Updates row through the `crud `__. @@ -2387,7 +2397,11 @@ def crud_update(self, space_name: str, key: int, operations: list=[], opts: dict """ assert isinstance(space_name, str) + if operations is None: + operations = [] assert isinstance(operations, list) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.update", space_name, key, operations, opts) @@ -2397,7 +2411,7 @@ def crud_update(self, space_name: str, key: int, operations: list=[], opts: dict return CrudResult(crud_resp[0]) - def crud_delete(self, space_name: str, key: int, opts: dict={}) -> CrudResult: + def crud_delete(self, space_name: str, key: int, opts: Optional[dict]=None) -> CrudResult: """ Deletes row through the `crud `__. @@ -2418,6 +2432,8 @@ def crud_delete(self, space_name: str, key: int, opts: dict={}) -> CrudResult: """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.delete", space_name, key, opts) @@ -2427,7 +2443,7 @@ def crud_delete(self, space_name: str, key: int, opts: dict={}) -> CrudResult: return CrudResult(crud_resp[0]) - def crud_replace(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_replace(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Replaces row through the `crud `__. @@ -2449,6 +2465,8 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], opts: dict={ assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.replace", space_name, values, opts) @@ -2458,7 +2476,7 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], opts: dict={ return CrudResult(crud_resp[0]) - def crud_replace_object(self, space_name: str, values: dict, opts: dict={}) -> CrudResult: + def crud_replace_object(self, space_name: str, values: dict, opts: Optional[dict]=None) -> CrudResult: """ Replaces object row through the `crud `__. @@ -2480,6 +2498,8 @@ def crud_replace_object(self, space_name: str, values: dict, opts: dict={}) -> C assert isinstance(space_name, str) assert isinstance(values, dict) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.replace_object", space_name, values, opts) @@ -2489,7 +2509,7 @@ def crud_replace_object(self, space_name: str, values: dict, opts: dict={}) -> C return CrudResult(crud_resp[0]) - def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Replaces batch rows through the `crud `__. @@ -2511,6 +2531,8 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: d assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.replace_many", space_name, values, opts) @@ -2527,7 +2549,7 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: d return res - def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Replaces batch object rows through the `crud `__. @@ -2549,6 +2571,8 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.replace_object_many", space_name, values, opts) @@ -2565,7 +2589,7 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], return res - def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: list=[], opts: dict={}) -> CrudResult: + def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: """ Upserts row through the `crud `__. @@ -2590,7 +2614,11 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: l assert isinstance(space_name, str) assert isinstance(values, (tuple, list)) + if operations is None: + operations = [] assert isinstance(operations, list) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.upsert", space_name, values, operations, opts) @@ -2600,7 +2628,7 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: l return CrudResult(crud_resp[0]) - def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], opts: dict={}) -> CrudResult: + def crud_upsert_object(self, space_name: str, values: dict, operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: """ Upserts object row through the `crud `__. @@ -2625,7 +2653,11 @@ def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], assert isinstance(space_name, str) assert isinstance(values, dict) + if operations is None: + operations = [] assert isinstance(operations, list) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.upsert_object", space_name, values, operations, opts) @@ -2635,7 +2667,7 @@ def crud_upsert_object(self, space_name: str, values: dict, operations: list=[], return CrudResult(crud_resp[0]) - def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Upserts batch rows through the `crud `__. @@ -2657,6 +2689,8 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] assert isinstance(space_name, str) assert isinstance(values_operation, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.upsert_many", space_name, values_operation, opts) @@ -2673,7 +2707,7 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] return res - def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], opts: dict={}) -> CrudResult: + def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: """ Upserts batch object rows through the `crud `__. @@ -2695,6 +2729,8 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple assert isinstance(space_name, str) assert isinstance(values_operation, (tuple, list)) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.upsert_object_many", space_name, values_operation, opts) @@ -2711,7 +2747,7 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple return res - def crud_select(self, space_name: str, conditions: list=[], opts: dict={}) -> CrudResult: + def crud_select(self, space_name: str, conditions: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: """ Selects rows through the `crud `__. @@ -2732,7 +2768,11 @@ def crud_select(self, space_name: str, conditions: list=[], opts: dict={}) -> Cr """ assert isinstance(space_name, str) - assert isinstance(conditions, (tuple, list)) + if conditions is None: + conditions = [] + assert isinstance(conditions, list) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.select", space_name, conditions, opts) @@ -2742,7 +2782,7 @@ def crud_select(self, space_name: str, conditions: list=[], opts: dict={}) -> Cr return CrudResult(crud_resp[0]) - def crud_min(self, space_name: str, index_name: str, opts: dict={}) -> CrudResult: + def crud_min(self, space_name: str, index_name: str, opts: Optional[dict]=None) -> CrudResult: """ Gets rows with minimum value in the specified index through the `crud `__. @@ -2763,6 +2803,8 @@ def crud_min(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.min", space_name, index_name, opts) @@ -2772,7 +2814,7 @@ def crud_min(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul return CrudResult(crud_resp[0]) - def crud_max(self, space_name: str, index_name: str, opts: dict={}) -> CrudResult: + def crud_max(self, space_name: str, index_name: str, opts: Optional[dict]=None) -> CrudResult: """ Gets rows with maximum value in the specified index through the `crud `__. @@ -2793,6 +2835,8 @@ def crud_max(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.max", space_name, index_name, opts) @@ -2802,7 +2846,7 @@ def crud_max(self, space_name: str, index_name: str, opts: dict={}) -> CrudResul return CrudResult(crud_resp[0]) - def crud_truncate(self, space_name: str, opts: dict={}) -> bool: + def crud_truncate(self, space_name: str, opts: Optional[dict]=None) -> bool: """ Truncate rows through the `crud `__. @@ -2820,6 +2864,8 @@ def crud_truncate(self, space_name: str, opts: dict={}) -> bool: """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.truncate", space_name, opts) @@ -2831,7 +2877,7 @@ def crud_truncate(self, space_name: str, opts: dict={}) -> bool: return crud_resp[0] - def crud_len(self, space_name: str, opts: dict={}) -> int: + def crud_len(self, space_name: str, opts: Optional[dict]=None) -> int: """ Gets the number of tuples in the space through the `crud `__. @@ -2849,6 +2895,8 @@ def crud_len(self, space_name: str, opts: dict={}) -> int: """ assert isinstance(space_name, str) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.len", space_name, opts) @@ -2860,7 +2908,7 @@ def crud_len(self, space_name: str, opts: dict={}) -> int: return crud_resp[0] - def crud_storage_info(self, opts: dict={}) -> dict: + def crud_storage_info(self, opts: Optional[dict]=None) -> dict: """ Gets storages status through the `crud `__. @@ -2874,6 +2922,8 @@ def crud_storage_info(self, opts: dict={}) -> dict: :exc:`~tarantool.error.DatabaseError` """ + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.storage_info", opts) @@ -2885,7 +2935,7 @@ def crud_storage_info(self, opts: dict={}) -> dict: return crud_resp[0] - def crud_count(self, space_name: str, conditions: list=[], opts: dict={}) -> int: + def crud_count(self, space_name: str, conditions: Optional[list]=None, opts: Optional[dict]=None) -> int: """ Gets rows count through the `crud `__. @@ -2906,7 +2956,11 @@ def crud_count(self, space_name: str, conditions: list=[], opts: dict={}) -> int """ assert isinstance(space_name, str) - assert isinstance(conditions, (tuple, list)) + if conditions is None: + conditions = [] + assert isinstance(conditions, list) + if opts is None: + opts = {} assert isinstance(opts, dict) crud_resp = call_crud(self, "crud.count", space_name, conditions, opts) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index ec23a9f7..25784143 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -1033,7 +1033,7 @@ def execute(self, query, params=None, *, mode=None): return self._send(mode, 'execute', query, params) - def crud_insert(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_insert(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_insert request on the pool server: inserts row through the @@ -1060,7 +1060,7 @@ def crud_insert(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_insert', space_name, values, opts) - def crud_insert_object(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_insert_object(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_insert_object request on the pool server: inserts object row through the @@ -1087,7 +1087,7 @@ def crud_insert_object(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_insert_object', space_name, values, opts) - def crud_insert_many(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_insert_many(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_insert_many request on the pool server: inserts batch rows through the @@ -1114,7 +1114,7 @@ def crud_insert_many(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_insert_many', space_name, values, opts) - def crud_insert_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_insert_object_many(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_insert_object_many request on the pool server: inserts batch object rows through the @@ -1141,7 +1141,7 @@ def crud_insert_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY) return self._send(mode, 'crud_insert_object_many', space_name, values, opts) - def crud_get(self, space_name, key, opts={}, *, mode=Mode.ANY): + def crud_get(self, space_name, key, opts=None, *, mode=Mode.ANY): """ Execute an crud_get request on the pool server: gets row through the @@ -1168,7 +1168,7 @@ def crud_get(self, space_name, key, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_get', space_name, key, opts) - def crud_update(self, space_name, key, operations=[], opts={}, *, mode=Mode.ANY): + def crud_update(self, space_name, key, operations=None, opts=None, *, mode=Mode.ANY): """ Execute an crud_update request on the pool server: updates row through the @@ -1198,7 +1198,7 @@ def crud_update(self, space_name, key, operations=[], opts={}, *, mode=Mode.ANY) return self._send(mode, 'crud_update', space_name, key, operations, opts) - def crud_delete(self, space_name, key, opts={}, *, mode=Mode.ANY): + def crud_delete(self, space_name, key, opts=None, *, mode=Mode.ANY): """ Execute an crud_delete request on the pool server: deletes row through the @@ -1225,7 +1225,7 @@ def crud_delete(self, space_name, key, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_delete', space_name, key, opts) - def crud_replace(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_replace(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_replace request on the pool server: replaces row through the @@ -1252,7 +1252,7 @@ def crud_replace(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_replace', space_name, values, opts) - def crud_replace_object(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_replace_object(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_replace_object request on the pool server: replaces object row through the @@ -1279,7 +1279,7 @@ def crud_replace_object(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_replace_object', space_name, values, opts) - def crud_replace_many(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_replace_many(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_replace_many request on the pool server: replaces batch rows through the @@ -1306,7 +1306,7 @@ def crud_replace_many(self, space_name, values, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_replace_many', space_name, values, opts) - def crud_replace_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY): + def crud_replace_object_many(self, space_name, values, opts=None, *, mode=Mode.ANY): """ Execute an crud_replace_object_many request on the pool server: replaces batch object rows through the @@ -1333,7 +1333,7 @@ def crud_replace_object_many(self, space_name, values, opts={}, *, mode=Mode.ANY return self._send(mode, 'crud_replace_object_many', space_name, values, opts) - def crud_upsert(self, space_name, values, operations=[], opts={}, *, mode=Mode.ANY): + def crud_upsert(self, space_name, values, operations=None, opts=None, *, mode=Mode.ANY): """ Execute an crud_upsert request on the pool server: upserts row through the @@ -1363,7 +1363,7 @@ def crud_upsert(self, space_name, values, operations=[], opts={}, *, mode=Mode.A return self._send(mode, 'crud_upsert', space_name, values, operations, opts) - def crud_upsert_object(self, space_name, values, operations=[], opts={}, *, mode=Mode.ANY): + def crud_upsert_object(self, space_name, values, operations=None, opts=None, *, mode=Mode.ANY): """ Execute an crud_upsert_object request on the pool server: upserts object row through the @@ -1393,7 +1393,7 @@ def crud_upsert_object(self, space_name, values, operations=[], opts={}, *, mode return self._send(mode, 'crud_upsert_object', space_name, values, operations, opts) - def crud_upsert_many(self, space_name, values_operation, opts={}, *, mode=Mode.ANY): + def crud_upsert_many(self, space_name, values_operation, opts=None, *, mode=Mode.ANY): """ Execute an crud_upsert_many request on the pool server: upserts batch rows through the @@ -1420,7 +1420,7 @@ def crud_upsert_many(self, space_name, values_operation, opts={}, *, mode=Mode.A return self._send(mode, 'crud_upsert_many', space_name, values_operation, opts) - def crud_upsert_object_many(self, space_name, values_operation, opts={}, *, mode=Mode.ANY): + def crud_upsert_object_many(self, space_name, values_operation, opts=None, *, mode=Mode.ANY): """ Execute an crud_upsert_object_many request on the pool server: upserts batch object rows through the @@ -1447,7 +1447,7 @@ def crud_upsert_object_many(self, space_name, values_operation, opts={}, *, mode return self._send(mode, 'crud_upsert_object_many', space_name, values_operation, opts) - def crud_select(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): + def crud_select(self, space_name, conditions=None, opts=None, *, mode=Mode.ANY): """ Execute an crud_select request on the pool server: selects rows through the @@ -1474,7 +1474,7 @@ def crud_select(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_select', space_name, conditions, opts) - def crud_min(self, space_name, index_name, opts={}, *, mode=Mode.ANY): + def crud_min(self, space_name, index_name, opts=None, *, mode=Mode.ANY): """ Execute an crud_min request on the pool server: gets rows with minimum value in the specified index through @@ -1501,7 +1501,7 @@ def crud_min(self, space_name, index_name, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_min', space_name, index_name, opts) - def crud_max(self, space_name, index_name, opts={}, *, mode=Mode.ANY): + def crud_max(self, space_name, index_name, opts=None, *, mode=Mode.ANY): """ Execute an crud_max request on the pool server: gets rows with maximum value in the specified index through @@ -1528,7 +1528,7 @@ def crud_max(self, space_name, index_name, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_max', space_name, index_name, opts) - def crud_len(self, space_name, opts={}, *, mode=Mode.ANY): + def crud_len(self, space_name, opts=None, *, mode=Mode.ANY): """ Execute an crud_len request on the pool server: gets the number of tuples in the space through @@ -1552,7 +1552,7 @@ def crud_len(self, space_name, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_len', space_name, opts) - def crud_storage_info(self, opts={}, *, mode=Mode.ANY): + def crud_storage_info(self, opts=None, *, mode=Mode.ANY): """ Execute an crud_storage_info request on the pool server: gets storages status through the @@ -1573,7 +1573,7 @@ def crud_storage_info(self, opts={}, *, mode=Mode.ANY): return self._send(mode, 'crud_storage_info', opts) - def crud_count(self, space_name, conditions=[], opts={}, *, mode=Mode.ANY): + def crud_count(self, space_name, conditions=None, opts=None, *, mode=Mode.ANY): """ Execute an crud_count request on the pool server: gets rows count through the @@ -1644,7 +1644,7 @@ def crud_unflatten_rows(self, rows, metadata, *, mode=Mode.ANY): return self._send(mode, 'crud_unflatten_rows', rows, metadata) - def crud_truncate(self, space_name, opts={}, *, mode=Mode.ANY): + def crud_truncate(self, space_name, opts=None, *, mode=Mode.ANY): """ Execute an crud_truncate request on the pool server: truncates rows through diff --git a/test/suites/test_push.py b/test/suites/test_push.py index a3d19c45..ed44bd21 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -74,7 +74,7 @@ def create_server(): # Callback for on_push arg (for testing purposes). -def push_callback(data, on_push_ctx=[]): +def push_callback(data, on_push_ctx): data[0][1] = data[0][1] + 1 on_push_ctx.append(data) From 30d47018af2af2cfdef68a4fe871cdbc96df56f4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:55:06 +0300 Subject: [PATCH 049/190] pylint: fix pointless-string-statement case Fix the only case of W0105 pointless-string-statement. Part of #270 --- tarantool/schema.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index e38e9ec1..47cf3821 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -10,12 +10,11 @@ ) import tarantool.const as const - +MAX_RECURSION_DEPTH = 32 """ Max possible known schema depth is 4 if foreign keys are used (since Tarantool 2.10), but there are no restrictions in protocol. """ -MAX_RECURSION_DEPTH = 32 class RecursionError(Error): From c6aa50d83b8b00153df48c4a38e8138c66cb426e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 16:57:07 +0300 Subject: [PATCH 050/190] pylint: fix unnecessary-pass cases Fix all cases of W0107 unnecessary-pass. Part of #270 --- tarantool/error.py | 9 --------- test/setup_command.py | 2 -- test/suites/lib/remote_tarantool_server.py | 1 - test/suites/lib/tarantool_server.py | 2 -- 4 files changed, 14 deletions(-) diff --git a/tarantool/error.py b/tarantool/error.py index cd981c21..57a42e51 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -271,7 +271,6 @@ class NetworkWarning(UserWarning): """ Warning related to network. """ - pass class SslError(DatabaseError): """ @@ -301,23 +300,17 @@ class ClusterDiscoveryWarning(UserWarning): """ Warning related to cluster discovery. """ - pass - class ClusterConnectWarning(UserWarning): """ Warning related to cluster pool connection. """ - pass - class PoolTolopogyWarning(UserWarning): """ Warning related to unsatisfying `box.info.ro`_ state of pool instances. """ - pass - class PoolTolopogyError(DatabaseError): """ @@ -326,8 +319,6 @@ class PoolTolopogyError(DatabaseError): .. _box.info.ro: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_info/ """ - pass - class CrudModuleError(DatabaseError): """ diff --git a/test/setup_command.py b/test/setup_command.py index 58e1b14e..2f0549b6 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -24,14 +24,12 @@ def initialize_options(self): Do nothing. setuptools requires to override this abstract method. """ - pass def finalize_options(self): """ Do nothing. setuptools requires to override this abstract method. """ - pass def run(self): """ diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index 5c5ab350..0b3cb0c1 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -123,7 +123,6 @@ def clean(self): """ Do nothing. """ - pass def __del__(self): self.admin.disconnect() diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 95c720e0..aa467b04 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -382,8 +382,6 @@ def touch_lock(self): RemoteTarantoolServer. """ - pass - def is_started(self): """ Is Tarantool server has need started. From 26eb6c9f09efcb04fd68d45a0f5ff1143d3ff700 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:02:31 +0300 Subject: [PATCH 051/190] pylint: fix unused-variable cases Fix all cases of W0612 unused-variable. Part of #270 --- setup.py | 1 - tarantool/connection_pool.py | 2 +- test/suites/test_pool.py | 14 +++++++------- test/suites/test_ssl.py | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index fb7340f9..4f4e9f3e 100755 --- a/setup.py +++ b/setup.py @@ -80,7 +80,6 @@ def get_dependencies(filename): root = os.path.dirname(os.path.realpath(__file__)) requirements = os.path.join(root, filename) - result = [] if os.path.isfile(requirements): with open(requirements) as file: return file.read().splitlines() diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 25784143..87dd494f 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -547,7 +547,7 @@ def _get_new_state(self, unit): try: conn.connect() except NetworkError as exc: - msg = f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}" + msg = f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}, reason: {repr(exc)}" warn(msg, ClusterConnectWarning) return InstanceState(Status.UNHEALTHY) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 801ebe44..120d7529 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -133,35 +133,35 @@ def get_port(self, mode): # Expect ANY iterate through all instances. any_ports_result = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): any_ports_result.add(get_port(self, tarantool.Mode.ANY)) self.assertSetEqual(any_ports_result, all_ports) # Expect RW iterate through all RW instances. rw_ports_result = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): rw_ports_result.add(get_port(self, tarantool.Mode.RW)) self.assertSetEqual(rw_ports_result, rw_ports) # Expect RO iterate through all RO instances. ro_ports_result = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): ro_ports_result.add(get_port(self, tarantool.Mode.RO)) self.assertSetEqual(ro_ports_result, ro_ports) # Expect PREFER_RW iterate through all RW instances if there is at least one. prefer_rw_ports_result = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): prefer_rw_ports_result.add(get_port(self, tarantool.Mode.PREFER_RW)) self.assertSetEqual(prefer_rw_ports_result, rw_ports) # Expect PREFER_RO iterate through all RO instances if there is at least one. prefer_ro_ports_result = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): prefer_ro_ports_result.add(get_port(self, tarantool.Mode.PREFER_RO)) self.assertSetEqual(prefer_ro_ports_result, ro_ports) @@ -179,7 +179,7 @@ def expect_rw_to_fail_if_there_are_no_rw(): # Expect PREFER_RW iterate through all instances if there are no RW. def expect_prefer_rw_iterate_through_all_instances_if_there_are_no_rw(): prefer_rw_ports_result_all_ro = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): prefer_rw_ports_result_all_ro.add(get_port(self, tarantool.Mode.PREFER_RW)) self.assertSetEqual(prefer_rw_ports_result_all_ro, all_ports) @@ -199,7 +199,7 @@ def expect_ro_to_fail_if_there_are_no_ro(): # Expect PREFER_RO iterate through all instances if there are no RO. def expect_prefer_ro_iterate_through_all_instances_if_there_are_no_ro(): prefer_ro_ports_result_all_rw = set() - for i in range(len(self.servers)): + for _ in range(len(self.servers)): prefer_ro_ports_result_all_rw.add(get_port(self, tarantool.Mode.PREFER_RO)) self.assertSetEqual(prefer_ro_ports_result_all_rw, all_ports) diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index b984126f..2cf64e5f 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -583,7 +583,7 @@ def test_pool(self): addrs = [] servers = [] - for i in range(cnt): + for _ in range(cnt): srv = TarantoolServer( transport=t.server_transport, ssl_key_file=t.server_key_file, From 3bf091e328aa2f22bca78345c1cb668665608cc2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:20:38 +0300 Subject: [PATCH 052/190] pylint: fix unused-import cases Fix all cases of W0611 unused-import. Part of #270 --- docs/source/conf.py | 1 - setup.py | 1 - tarantool/connection.py | 1 - tarantool/connection_pool.py | 2 -- tarantool/msgpack_ext/datetime.py | 1 - tarantool/request.py | 2 -- tarantool/utils.py | 3 +-- test/setup_command.py | 3 --- test/suites/lib/skip.py | 1 - test/suites/test_datetime.py | 4 +--- test/suites/test_decimal.py | 1 - test/suites/test_error_ext.py | 2 -- test/suites/test_interval.py | 3 --- test/suites/test_pool.py | 1 - test/suites/test_uuid.py | 2 -- 15 files changed, 2 insertions(+), 26 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 308c14be..286e5da8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,7 +12,6 @@ """ # pylint: disable=invalid-name -import sys, os import tarantool # If extensions (or modules to document with autodoc) are in another directory, diff --git a/setup.py b/setup.py index 4f4e9f3e..46bf4cbb 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ import codecs import os -import re try: from setuptools import setup, find_packages diff --git a/tarantool/connection.py b/tarantool/connection.py index 72b44056..1b814557 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -71,7 +71,6 @@ IPROTO_FEATURE_TRANSACTIONS, IPROTO_FEATURE_ERROR_EXTENSION, IPROTO_FEATURE_WATCHERS, - IPROTO_AUTH_TYPE, IPROTO_CHUNK, AUTH_TYPE_CHAP_SHA1, AUTH_TYPE_PAP_SHA256, diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 87dd494f..6b701587 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -19,8 +19,6 @@ POOL_INSTANCE_RECONNECT_MAX_ATTEMPTS, POOL_REFRESH_DELAY, SOCKET_TIMEOUT, - DEFAULT_SSL_PASSWORD, - DEFAULT_SSL_PASSWORD_FILE, ) from tarantool.error import ( ClusterConnectWarning, diff --git a/tarantool/msgpack_ext/datetime.py b/tarantool/msgpack_ext/datetime.py index a187940e..8964245e 100644 --- a/tarantool/msgpack_ext/datetime.py +++ b/tarantool/msgpack_ext/datetime.py @@ -40,7 +40,6 @@ from tarantool.msgpack_ext.types.datetime import ( NSEC_IN_SEC, - SEC_IN_MIN, Datetime, ) import tarantool.msgpack_ext.types.timezones as tt_timezones diff --git a/tarantool/request.py b/tarantool/request.py index 9cb0e055..a04f4a28 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -3,13 +3,11 @@ send pre-build request objects. """ -import sys import hashlib from collections.abc import Sequence, Mapping import msgpack -from tarantool.error import DatabaseError from tarantool.const import ( IPROTO_REQUEST_TYPE, IPROTO_SYNC, diff --git a/tarantool/utils.py b/tarantool/utils.py index d09adb1e..96670f02 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -3,9 +3,8 @@ """ from base64 import decodebytes as base64_decode -from dataclasses import dataclass, field +from dataclasses import dataclass import typing -import sys import uuid ENCODING_DEFAULT = "utf-8" diff --git a/test/setup_command.py b/test/setup_command.py index 2f0549b6..a39bb599 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -3,11 +3,8 @@ This module describes class implementing `python setup.py test`. """ -import os -import sys import unittest from distutils.errors import DistutilsError -from glob import glob import setuptools diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index c39d0151..d3b08fc8 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -3,7 +3,6 @@ """ import functools -import re import sys import pkg_resources diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index eb17e092..e1b90e39 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -6,13 +6,11 @@ import sys import re import unittest -import warnings import msgpack -import pandas import tarantool -from tarantool.error import MsgpackError, MsgpackWarning +from tarantool.error import MsgpackError from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 6b3b8fb8..34261c68 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -6,7 +6,6 @@ import sys import unittest import decimal -import warnings import msgpack diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 41b664de..c59fc19f 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -5,8 +5,6 @@ import sys import unittest -import uuid -import warnings import pkg_resources import msgpack diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 64fdbc85..f0b5a8d0 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -6,11 +6,8 @@ import re import sys import unittest -import warnings import msgpack -import pandas -import pytz import tarantool from tarantool.error import MsgpackError diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 120d7529..1e2bf31c 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -13,7 +13,6 @@ from tarantool.error import ( ClusterConnectWarning, DatabaseError, - NetworkError, NetworkWarning, PoolTolopogyError, PoolTolopogyWarning, diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index b136b1fe..52ab5bda 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -6,12 +6,10 @@ import sys import unittest import uuid -import warnings import msgpack import tarantool -from tarantool.error import MsgpackError, MsgpackWarning from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook From 976fe09b9a0a20f5903f7000a8b43624f19e50a3 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:31:01 +0300 Subject: [PATCH 053/190] pylint: disable unused-argument cases Disable both cases of W0613 unused-argument. In case of a server, it sounds weird to have different __new__ and __init__ API in a class. In case of unittest setup, arguments are the part of unittest that may be useful later. Part of #270 --- test/suites/__init__.py | 1 + test/suites/lib/tarantool_server.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/test/suites/__init__.py b/test/suites/__init__.py index f3ec7b95..e1321866 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -39,6 +39,7 @@ def load_tests(loader, tests, pattern): """ Add suites to test run. """ + # pylint: disable=unused-argument suite = unittest.TestSuite() for testc in test_cases: diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index aa467b04..8d809c65 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -186,6 +186,8 @@ def __new__(cls, ssl_password_file=None, create_unix_socket=False, auth_type=None): + # pylint: disable=unused-argument + if os.name == 'nt': return RemoteTarantoolServer() return super(TarantoolServer, cls).__new__(cls) From b8c7bb1204a2a78f59910fd9142ca7deb375509a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:36:52 +0300 Subject: [PATCH 054/190] api: only named on_push and on_push_ctx Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. We already allow only named push parameters for every other call. --- CHANGELOG.md | 5 +++++ tarantool/connection.py | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c49812..58caebf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Changed +- **Breaking**: Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. + ## 0.12.1 - 2023-02-28 ### Changed diff --git a/tarantool/connection.py b/tarantool/connection.py index 1b814557..53518c38 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -196,7 +196,7 @@ def eval(self, expr, *args, on_push=None, on_push_ctx=None): raise NotImplementedError @abc.abstractmethod - def replace(self, space_name, values, on_push=None, on_push_ctx=None): + def replace(self, space_name, values, *, on_push=None, on_push_ctx=None): """ Reference implementation: :meth:`~tarantool.Connection.replace`. """ @@ -204,7 +204,7 @@ def replace(self, space_name, values, on_push=None, on_push_ctx=None): raise NotImplementedError @abc.abstractmethod - def insert(self, space_name, values, on_push=None, on_push_ctx=None): + def insert(self, space_name, values, *, on_push=None, on_push_ctx=None): """ Reference implementation: :meth:`~tarantool.Connection.insert`. """ @@ -1406,7 +1406,7 @@ def eval(self, expr, *args, on_push=None, on_push_ctx=None): response = self._send_request(request, on_push, on_push_ctx) return response - def replace(self, space_name, values, on_push=None, on_push_ctx=None): + def replace(self, space_name, values, *, on_push=None, on_push_ctx=None): """ Execute a REPLACE request: `replace`_ a tuple in the space. Doesn't throw an error if there is no tuple with the specified @@ -1628,7 +1628,7 @@ def subscribe(self, cluster_uuid, server_uuid, vclock=None): return self.close() # close connection after SUBSCRIBE - def insert(self, space_name, values, on_push=None, on_push_ctx=None): + def insert(self, space_name, values, *, on_push=None, on_push_ctx=None): """ Execute an INSERT request: `insert`_ a tuple to the space. Throws an error if there is already a tuple with the same From 368e686a6aa288254deb349da0f65489b337623e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:38:50 +0300 Subject: [PATCH 055/190] pylint: fix arguments-differ case Fix the only remaining case of W0221 arguments-differ. Part of #270 --- test/suites/test_connection.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 77814a0b..37219356 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -155,10 +155,9 @@ def my_unpacker_factory(_): resp = self.con.eval("return {1, 2, 3}") self.assertIsInstance(resp[0], tuple) - @classmethod - def tearDown(cls): - if hasattr(cls, 'con'): - cls.con.close() + def tearDown(self): + if hasattr(self, 'con'): + self.con.close() @classmethod def tearDownClass(cls): From a184485a37aaf54c1a4d0a2144c9991b8a502496 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:45:06 +0300 Subject: [PATCH 056/190] pylint: fix attribute-defined-outside-init cases Fix all cases of W0201 attribute-defined-outside-init. Part of #270 --- tarantool/connection.py | 3 +++ tarantool/mesh_connection.py | 1 + test/suites/lib/tarantool_server.py | 11 +++++++---- test/suites/test_connection.py | 1 + test/suites/test_pool.py | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 53518c38..30d7e661 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -837,6 +837,9 @@ def __init__(self, host, port, self._unpacker_factory_impl = unpacker_factory self._client_auth_type = auth_type self._server_auth_type = None + self.version_id = None + self.uuid = None + self._salt = None if connect_now: self.connect() diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 535e6412..10593e6d 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -209,6 +209,7 @@ def __init__(self, addrs): :paramref:`~tarantool.ConnectionPool.params.addrs`. :type addrs: :obj:`list` of :obj:`dict` """ + self.pos = None self.update(addrs) def update(self, new_addrs): diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 8d809c65..501c7100 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -126,7 +126,7 @@ def binary(self): Get Tarantool binary used to start the server. """ - if not hasattr(self, '_binary'): + if self._binary is None: self._binary = self.find_exe() return self._binary @@ -160,7 +160,7 @@ def log_des(self): Get server log file descriptor. """ - if not hasattr(self, '_log_des'): + if self._log_des is None: self._log_des = open(self.logfile_path, 'a') return self._log_des @@ -170,11 +170,12 @@ def log_des(self): Set server log file descriptor. """ - if not hasattr(self, '_log_des'): + if self._log_des is None: return if not self._log_des.closed: self._log_des.close() - delattr(self, '_log_des') + + self._log_des = None def __new__(cls, transport=None, @@ -229,6 +230,8 @@ def __init__(self, self.ssl_password = ssl_password self.ssl_password_file = ssl_password_file self.auth_type = auth_type + self._binary = None + self._log_des = None def find_exe(self): """ diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 37219356..ee619715 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -54,6 +54,7 @@ def setUpClass(cls): parts = {2, 'varbinary'}, unique = true}) """) + cls.con = None def setUp(self): # prevent a remote tarantool from clean our session diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 1e2bf31c..50bca042 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -84,6 +84,7 @@ def setUp(self): # Create five servers and extract helpful fields for tests. self.servers = [] self.addrs = [] + self.pool = None self.servers_count = 5 for i in range(self.servers_count): srv = create_server(i) From 8dbae6ef3f530d18063396e49edd96532f6bd263 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:49:01 +0300 Subject: [PATCH 057/190] pylint: fix protected-access cases Fix several cases of W0212 protected-access. Part of #270 --- tarantool/connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 30d7e661..2a01eb40 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1174,9 +1174,9 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): self.update_schema(exc.schema_version) continue - while response._code == IPROTO_CHUNK: + while response.code == IPROTO_CHUNK: if on_push is not None: - on_push(response._data, on_push_ctx) + on_push(response.data, on_push_ctx) response = request.response_class(self, self._read_response()) return response From 7e6139e7eec4441de2ed7eaac2bd438f86df2418 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:51:05 +0300 Subject: [PATCH 058/190] pylint: disable protected-access for warning impl Disable W0212 protected-access for our warning implementation. On the first glance it doesn't seem like we can get rid of _getframe here with the same implementation. Part of #270 --- tarantool/error.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tarantool/error.py b/tarantool/error.py index 57a42e51..adc4aecc 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -380,6 +380,7 @@ def warn(message, warning_class): :param warning_class: Warning class. :type warning_class: :class:`~tarantool.error.Warning` """ + # pylint: disable=protected-access frame = sys._getframe(2) module_name = frame.f_globals.get("__name__") From 1fdcdb316414913c3604749f656e68d09532819a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:54:23 +0300 Subject: [PATCH 059/190] pylint: disable protected-access for unittest workaround Disable W0212 protected-access for unittest workaround. The issue isn't fixed i modern Python yet [1]. 1. https://github.com/python/cpython/issues/66019 Part of #270 --- test/suites/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/__init__.py b/test/suites/__init__.py index e1321866..09e84821 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -52,4 +52,4 @@ def load_tests(loader, tests, pattern): os.chdir(__tmp) # Workaround to disable unittest output truncating -__import__('sys').modules['unittest.util']._MAX_LENGTH = 99999 +__import__('sys').modules['unittest.util']._MAX_LENGTH = 99999 # pylint: disable=protected-access From 01806bac5d84babb47fa9a8ff0a335b813fdfc07 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:56:38 +0300 Subject: [PATCH 060/190] pylint: disable protected-access for tests Disable W0212 protected-access for tests. We allow to mess with internals for unit testing or creating error scenarios. Part of #270 --- test/suites/test_crud.py | 2 +- test/suites/test_dbapi.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_error_ext.py | 2 +- test/suites/test_interval.py | 2 +- test/suites/test_protocol.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index a57e8929..c7eed96b 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -1,7 +1,7 @@ """ This module tests integration with tarantool/crud. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import re import sys diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index c001a42c..29341ce4 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -1,7 +1,7 @@ """ This module tests compatibility with DBAPI standards. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import sys import unittest diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 4d1bad6e..672e9663 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,7 +1,7 @@ """ This module tests basic data operations. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import sys import unittest diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index c59fc19f..2c2bab12 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,7 +1,7 @@ """ This module tests work with extended error type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import sys import unittest diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index f0b5a8d0..26c39c88 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,7 +1,7 @@ """ This module tests work with datetime interval type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import re import sys diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index c966c210..2553a23f 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,7 +1,7 @@ """ This module tests connection authentication. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access import sys import unittest From 22ccdb174fa8954c5f7b9fbdbdeb486eff7957e9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 17:58:21 +0300 Subject: [PATCH 061/190] pylint: fix pointless-string-statement case Fix the only case of W0237 arguments-renamed. Part of #270 --- tarantool/response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tarantool/response.py b/tarantool/response.py index aae3539f..ee06ca58 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -191,7 +191,7 @@ def index(self, *args): raise InterfaceError("Trying to access data when there's no data") return self._data.index(*args) - def count(self, item): + def count(self, value): """ Refer to :class:`collections.abc.Sequence`. @@ -200,7 +200,7 @@ def count(self, item): if self._data is None: raise InterfaceError("Trying to access data when there's no data") - return self._data.count(item) + return self._data.count(value) @property def rowcount(self): From dc505df6e7bd38060ff35ed2f6815c06b3381d1c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:06:19 +0300 Subject: [PATCH 062/190] pylint: fix abstract-method cases Fix all cases of W0223 abstract-method. Part of #270 --- test/suites/test_dbapi.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 29341ce4..0c3c8508 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -53,6 +53,14 @@ def tearDownClass(cls): cls.srv.stop() cls.srv.clean() + def help_nextset_setUp(self, cur): + # pylint: disable=unused-argument + pass + + def help_nextset_tearDown(self, cur): + # pylint: disable=unused-argument + pass + def test_rowcount(self): con = self._connect() try: From c3c6512e82af40547f0e499d013563ff7cab0228 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:09:19 +0300 Subject: [PATCH 063/190] pylint: fix bad-indentation cases Fix all cases of W0311 bad-indentation. Part of #270 --- test/suites/test_dml.py | 2 +- test/suites/test_schema.py | 194 ++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 98 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 672e9663..e6d9b1a3 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -140,7 +140,7 @@ def test_03_delete(self): # Check that field has no meaning, yet. with self.assertRaisesRegex(tarantool.DatabaseError, '(19, .*)'): - self.con.delete('space_1', [1, 'tuple_21']) + self.con.delete('space_1', [1, 'tuple_21']) self.assertSequenceEqual(self.con.select('space_1', [21], index='primary'), [[21, 1, 'tuple_21']]) def test_04_replace(self): diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index c9a4bbc0..b7b08006 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -460,108 +460,108 @@ def test_07_schema_version_update(self): } 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]], - } + # 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']: - 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 exc: - self.assertEqual(exc.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']: - 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']: - with self.subTest(name=method_case): - if isinstance(con, tarantool.ConnectionPool) and method_case == 'space': - continue - testing_function = getattr(con, method_case) + # 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']: + 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: - resp = testing_function( + _ = testing_function( *self.testing_methods['unavailable'][method_case]['input'], mode=mode) else: - resp = testing_function( + _ = 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']: - 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) + except NotSupportedError as exc: + self.assertEqual(exc.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']: + 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']: + 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']: + 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) @@ -591,7 +591,7 @@ def test_10_foreign_key_valid_replace(self): def test_11_foreign_key_invalid_replace(self): with self.assertRaisesRegex(tarantool.DatabaseError, 'foreign tuple was not found'): - self.con.replace('constr_tester_2', [2, 999, 623]) + self.con.replace('constr_tester_2', [2, 999, 623]) @classmethod def tearDownClass(cls): From 250f63c74c34aac07da26cb329dd300509651327 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:16:57 +0300 Subject: [PATCH 064/190] pylint: fix unnecessary-semicolon cases Fix all cases of W0301 unnecessary-semicolon. Part of #270 --- test/suites/test_protocol.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 2553a23f..9aef72b2 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -38,7 +38,7 @@ def setUp(self): def test_00_greeting_1_6(self): buf = "Tarantool 1.6.6 \n" + \ - "AtQnb9SAIaKazZZy9lJKvK3urtbjCEJndhRVbslSPGc= \n"; + "AtQnb9SAIaKazZZy9lJKvK3urtbjCEJndhRVbslSPGc= \n" greeting = greeting_decode(buf.encode()) self.assertEqual(greeting.version_id, version_id(1, 6, 6)) self.assertEqual(greeting.protocol, "Binary") @@ -47,7 +47,7 @@ def test_00_greeting_1_6(self): def test_01_greeting_1_6_with_tag(self): buf = "Tarantool 1.6.6-232-gcf47324 \n" + \ - "AtQnb9SAIaKazZZy9lJKvK3urtbjCEJndhRVbslSPGc= \n"; + "AtQnb9SAIaKazZZy9lJKvK3urtbjCEJndhRVbslSPGc= \n" greeting = greeting_decode(buf.encode()) self.assertEqual(greeting.version_id, version_id(1, 6, 6)) self.assertEqual(greeting.protocol, "Binary") @@ -56,7 +56,7 @@ def test_01_greeting_1_6_with_tag(self): def test_02_greeting_1_6_console(self): buf = "Tarantool 1.6.6-132-g82f5424 (Lua console) \n" + \ - "type 'help' for interactive help \n"; + "type 'help' for interactive help \n" greeting = greeting_decode(buf.encode()) self.assertEqual(greeting.version_id, version_id(1, 6, 6)) self.assertEqual(greeting.protocol, "Lua console") @@ -65,7 +65,7 @@ def test_02_greeting_1_6_console(self): def test_03_greeting_1_6_7(self): buf = "Tarantool 1.6.7 (Binary) 52dc2837-8001-48fe-bdce-c493c04599ce \n" + \ - "Z+2F+VRlyK1nKT82xQtxqEggMtkTK5RtPYf27JryRas= \n"; + "Z+2F+VRlyK1nKT82xQtxqEggMtkTK5RtPYf27JryRas= \n" greeting = greeting_decode(buf.encode()) self.assertEqual(greeting.version_id, version_id(1, 6, 7)) self.assertEqual(greeting.protocol, "Binary") From 6a8b9a5ce0ee9f6029cdfd1c3882161993ca6aeb Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:20:48 +0300 Subject: [PATCH 065/190] pylint: disable fixme cases Disable W0212 fixme cases. Some of then are related to modules which are deprecated (mesh_connection) or doesn't any active support (dbapi). For now, we also ignore the suggestions to improve the tests. Part of #270 --- tarantool/dbapi.py | 1 + tarantool/mesh_connection.py | 1 + test/suites/test_dbapi.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_schema.py | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index ec145d58..45bb5cd0 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -3,6 +3,7 @@ .. _PEP-249: http://www.python.org/dev/peps/pep-0249/ """ +# pylint: disable=fixme from tarantool.connection import Connection as BaseConnection from tarantool.error import * diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 10593e6d..0540293a 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -1,6 +1,7 @@ """ This module provides API for interaction with Tarantool servers cluster. """ +# pylint: disable=fixme import time diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 0c3c8508..39267ca8 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -1,7 +1,7 @@ """ This module tests compatibility with DBAPI standards. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme import sys import unittest diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index e6d9b1a3..6dcb732d 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,7 +1,7 @@ """ This module tests basic data operations. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme import sys import unittest diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index b7b08006..bf5c79b4 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,fixme import sys import unittest From 33a2cec6168ba051dcab39053ca2a7c57f3ff2b7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:30:49 +0300 Subject: [PATCH 066/190] api: migrate to built-in Warning Use built-in warning instead of the default one. Warning isn't the part of the core module API, so this change isn't treated as a breaking one: the only thing that would change is the traceback info. At the time of writing of the DB-API 2.0 in 1999, the warning framework in Python did not yet exist, so we should workaround its expose [1]. 1. https://peps.python.org/pep-0249/#id59 Part of #270 --- CHANGELOG.md | 1 + tarantool/connection.py | 1 - tarantool/dbapi.py | 2 ++ tarantool/error.py | 7 ------- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58caebf6..4fcc8267 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **Breaking**: Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. +- Migrate to built-in `Warning` instead of a custom one. ## 0.12.1 - 2023-02-28 diff --git a/tarantool/connection.py b/tarantool/connection.py index 2a01eb40..e06c1dc7 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -94,7 +94,6 @@ CrudModuleError, CrudModuleManyError, SchemaReloadException, - Warning, warn ) from tarantool.schema import Schema diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 45bb5cd0..9b75e180 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -8,6 +8,8 @@ from tarantool.connection import Connection as BaseConnection from tarantool.error import * +Warning = Warning # pylint: disable=redefined-builtin,self-assigning-variable + # pylint: disable=invalid-name paramstyle = 'named' apilevel = "2.0" diff --git a/tarantool/error.py b/tarantool/error.py index adc4aecc..254205b7 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -15,13 +15,6 @@ import warnings - -class Warning(Exception): - """ - Exception raised for important warnings - like data truncations while inserting, etc. - """ - class Error(Exception): """ Base class for error exceptions. From 8073dc394a6f1aba2a27ba0923ae32b42f6258d5 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:31:18 +0300 Subject: [PATCH 067/190] api: migrate to built-in RecursionError Use built-in warning instead of the default one. RecursionError isn't the part of the core module API, so this change isn't treated as a breaking one: the only thing that would change is the traceback info. Part of #270 --- CHANGELOG.md | 1 + tarantool/schema.py | 14 +------------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fcc8267..c0d890e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **Breaking**: Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. - Migrate to built-in `Warning` instead of a custom one. +- Migrate to built-in `RecursionError` instead of a custom one. ## 0.12.1 - 2023-02-28 diff --git a/tarantool/schema.py b/tarantool/schema.py index 47cf3821..cd0e7e2b 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -4,7 +4,6 @@ """ from tarantool.error import ( - Error, SchemaError, DatabaseError ) @@ -16,17 +15,6 @@ Tarantool 2.10), but there are no restrictions in protocol. """ - -class RecursionError(Error): - """ - Report the situation when max recursion depth is reached. - - This is an internal error of - :func:`~tarantool.schema.to_unicode_recursive` caller and it should - be re-raised properly by the caller. - """ - - def to_unicode(string): """ Decode :obj:`bytes` to unicode :obj:`str`. @@ -57,7 +45,7 @@ def to_unicode_recursive(value, max_depth): :return: The same structure where all :obj:`bytes` are replaced with unicode :obj:`str`. - :raise: :exc:`~tarantool.schema.RecursionError` + :raise: :exc:`~RecursionError` :meta private: """ From fc9c62aadab2a02d7ba6b8ff6a2aa0662418e2f2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 11:57:26 +0300 Subject: [PATCH 068/190] pylint: disable redefined-builtin for sphinx Disable the case of W0622 redefined-builtin for sphinx. It is the part of the API. Part of #270 --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 286e5da8..4e08ccea 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,7 +10,7 @@ All configuration values have a default; values that are commented out serve to show the default. """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name,redefined-builtin import tarantool From 2fafeb36f9adfddf8a6b131e2d025f9904963a5c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:39:47 +0300 Subject: [PATCH 069/190] pylint: fix redefined-builtin case Fix the only remaining case of W0622 redefined-builtin. Part of #270 --- test/suites/lib/tarantool_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index cf57ea73..04406792 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -53,7 +53,7 @@ def __enter__(self): self.connect() return self - def __exit__(self, type, value, traceback): + def __exit__(self, exception_type, exception_value, exception_traceback): self.disconnect() def __call__(self, command): From 0f2f8a8baef48f7a3c651da06b0d393bb9b187b9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:47:40 +0300 Subject: [PATCH 070/190] test: remove outdated workarounds The issue [1] has been resolved and we already use the newer version. 1. https://github.com/baztian/dbapi-compliance/issues/5 --- test/suites/test_dbapi.py | 40 --------------------------------------- 1 file changed, 40 deletions(-) diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 39267ca8..da104c03 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -138,43 +138,3 @@ def test_setoutputsize(self): # Do nothing @unittest.skip('Not implemented') def test_description(self): pass - - def test_ExceptionsAsConnectionAttributes(self): - # Workaround for https://github.com/baztian/dbapi-compliance/issues/5 - - # OPTIONAL EXTENSION - # Test for the optional DB API 2.0 extension, where the exceptions - # are exposed as attributes on the Connection object - # I figure this optional extension will be implemented by any - # driver author who is using this test suite, so it is enabled - # by default. - drv = self.driver - con = self._connect() - try: - dbapi20._failUnless(self,con.Warning is drv.Warning) - dbapi20._failUnless(self,con.Error is drv.Error) - dbapi20._failUnless(self,con.InterfaceError is drv.InterfaceError) - dbapi20._failUnless(self,con.DatabaseError is drv.DatabaseError) - dbapi20._failUnless(self,con.OperationalError is drv.OperationalError) - dbapi20._failUnless(self,con.IntegrityError is drv.IntegrityError) - dbapi20._failUnless(self,con.InternalError is drv.InternalError) - dbapi20._failUnless(self,con.ProgrammingError is drv.ProgrammingError) - dbapi20. _failUnless(self,con.NotSupportedError is drv.NotSupportedError) - finally: - con.close() - - - def test_rollback(self): - # Workaround for https://github.com/baztian/dbapi-compliance/issues/5 - - con = self._connect() - try: - # If rollback is defined, it should either work or throw - # the documented exception - if hasattr(con,'rollback'): - try: - con.rollback() - except self.driver.NotSupportedError: - pass - finally: - con.close() From 4e2286925631374b38481fa17e4a99ca4363699c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 18:48:52 +0300 Subject: [PATCH 071/190] pylint: fix unused-wildcard-import case Fix the only case of W0614 unused-wildcard-import. Remaining tarantool.error import are the part of the DBAPI requirements, so they cannot be omitted. Part of #270 --- tarantool/dbapi.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 9b75e180..334673f1 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -3,10 +3,19 @@ .. _PEP-249: http://www.python.org/dev/peps/pep-0249/ """ -# pylint: disable=fixme +# pylint: disable=fixme,unused-import from tarantool.connection import Connection as BaseConnection -from tarantool.error import * +from tarantool.error import ( + Error, + InterfaceError, + DatabaseError, + OperationalError, + IntegrityError, + InternalError, + ProgrammingError, + NotSupportedError, +) Warning = Warning # pylint: disable=redefined-builtin,self-assigning-variable From 40d123057066058b39c2b6975a079b698a6b3d27 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 28 Mar 2023 19:00:16 +0300 Subject: [PATCH 072/190] pylint: disable cell-var-from-loop cases in tests Disable all cases of W0640 cell-var-from-loop in tests. Extracting the lambda outside the test subcase doesn't sounds like an improvement. Part of #270 --- test/suites/test_datetime.py | 2 ++ test/suites/test_decimal.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index e1b90e39..d38fe1f1 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -144,6 +144,8 @@ def test_datetime_class_api_wth_tz(self): } def test_datetime_class_invalid_init(self): + # pylint: disable=cell-var-from-loop + for name, case in self.datetime_class_invalid_init_cases.items(): with self.subTest(msg=name): self.assertRaisesRegex( diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 34261c68..4598ea2a 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -300,6 +300,8 @@ def test_tarantool_encode(self): } def test_msgpack_encode_error(self): + # pylint: disable=cell-var-from-loop + for name, case in self.error_cases.items(): with self.subTest(msg=name): msg = 'Decimal cannot be encoded: Tarantool decimal ' + \ @@ -310,6 +312,8 @@ def test_msgpack_encode_error(self): @skip_or_run_decimal_test def test_tarantool_encode_error(self): + # pylint: disable=cell-var-from-loop + for name, case in self.error_cases.items(): with self.subTest(msg=name): msg = 'Decimal cannot be encoded: Tarantool decimal ' + \ @@ -377,6 +381,8 @@ def test_tarantool_encode_error(self): } def test_msgpack_encode_with_precision_loss(self): + # pylint: disable=cell-var-from-loop + for name, case in self.precision_loss_cases.items(): with self.subTest(msg=name): msg = 'Decimal encoded with loss of precision: ' + \ @@ -393,6 +399,8 @@ def test_msgpack_encode_with_precision_loss(self): @skip_or_run_decimal_test def test_tarantool_encode_with_precision_loss(self): + # pylint: disable=cell-var-from-loop + for name, case in self.precision_loss_cases.items(): with self.subTest(msg=name): msg = 'Decimal encoded with loss of precision: ' + \ From 131d336e6ac60168331eef1f3a6d0f344a40e554 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:36:13 +0300 Subject: [PATCH 073/190] pylint: fix raise-missing-from cases Fix all cases of W0707 raise-missing-from. Part of #270 --- CHANGELOG.md | 1 + tarantool/connection.py | 16 ++++++++-------- tarantool/msgpack_ext/interval.py | 2 +- tarantool/schema.py | 8 ++++---- tarantool/utils.py | 2 +- test/suites/lib/tarantool_server.py | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d890e3..aab01e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Breaking**: Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. - Migrate to built-in `Warning` instead of a custom one. - Migrate to built-in `RecursionError` instead of a custom one. +- Collect full exception traceback. ## 0.12.1 - 2023-02-28 diff --git a/tarantool/connection.py b/tarantool/connection.py index e06c1dc7..5369319e 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -897,7 +897,7 @@ def connect_tcp(self): self._socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) except socket.error as exc: self.connected = False - raise NetworkError(exc) + raise NetworkError(exc) from exc def connect_unix(self): """ @@ -920,7 +920,7 @@ def connect_unix(self): self._socket.settimeout(self.socket_timeout) except socket.error as exc: self.connected = False - raise NetworkError(exc) + raise NetworkError(exc) from exc def wrap_socket_ssl(self): """ @@ -975,7 +975,7 @@ def wrap_socket_ssl(self): except SslError as exc: raise exc except Exception as exc: - raise SslError(exc) + raise SslError(exc) from exc def _ssl_load_cert_chain(self, context): """ @@ -1079,7 +1079,7 @@ def connect(self): raise exc except Exception as exc: self.connected = False - raise NetworkError(exc) + raise NetworkError(exc) from exc def _recv(self, to_read): """ @@ -1098,19 +1098,19 @@ def _recv(self, to_read): while to_read > 0: try: tmp = self._socket.recv(to_read) - except OverflowError: + except OverflowError as exc: self._socket.close() err = socket.error( errno.ECONNRESET, "Packet too large. Closing connection to server" ) - raise NetworkError(err) - except socket.error: + raise NetworkError(err) from exc + except socket.error as exc: err = socket.error( errno.ECONNRESET, "Lost connection to server during query" ) - raise NetworkError(err) + raise NetworkError(err) from exc else: if len(tmp) == 0: err = socket.error( diff --git a/tarantool/msgpack_ext/interval.py b/tarantool/msgpack_ext/interval.py index a45a8213..01d35ea4 100644 --- a/tarantool/msgpack_ext/interval.py +++ b/tarantool/msgpack_ext/interval.py @@ -127,7 +127,7 @@ def decode(data, unpacker): try: value = Adjust(value) except ValueError as exc: - raise MsgpackError(exc) + raise MsgpackError(exc) from exc kwargs[id_map[field_id]] = value diff --git a/tarantool/schema.py b/tarantool/schema.py index cd0e7e2b..11992363 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -98,7 +98,7 @@ def __init__(self, index_row, space): parts_raw = to_unicode_recursive(index_row[5], MAX_RECURSION_DEPTH) except RecursionError as exc: errmsg = 'Unexpected index parts structure: ' + str(exc) - raise SchemaError(errmsg) + raise SchemaError(errmsg) from exc if isinstance(parts_raw, (list, tuple)): for val in parts_raw: if isinstance(val, dict): @@ -155,7 +155,7 @@ def __init__(self, space_row, schema): format_raw = to_unicode_recursive(space_row[6], MAX_RECURSION_DEPTH) except RecursionError as exc: errmsg = 'Unexpected space format structure: ' + str(exc) - raise SchemaError(errmsg) + raise SchemaError(errmsg) from exc for part_id, part in enumerate(format_raw): part['id'] = part_id self.format[part['name']] = part @@ -436,10 +436,10 @@ def get_field(self, space, field): _space = self.get_space(space) try: return _space.format[field] - except: + except KeyError as exc: kind = 'name' if isinstance(field, str) else 'id' errmsg = f"There's no field with {kind} '{field}' in space '{_space.name}'" - raise SchemaError(errmsg) + raise SchemaError(errmsg) from exc return field diff --git a/tarantool/utils.py b/tarantool/utils.py index 96670f02..c10de9c2 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -141,4 +141,4 @@ def greeting_decode(greeting_buf): return result except Exception as exc: print('exx', exc) - raise ValueError("Invalid greeting: " + str(greeting_buf)) + raise ValueError("Invalid greeting: " + str(greeting_buf)) from exc diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 501c7100..6c2d3946 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -148,8 +148,8 @@ def _admin(self, port): try: int(port) - except ValueError: - raise ValueError(f"Bad port number: '{port}'") + except ValueError as exc: + raise ValueError(f"Bad port number: '{port}'") from exc if hasattr(self, 'admin'): del self.admin self.admin = TarantoolAdmin('0.0.0.0', port) From df253d2d6a357c867eb97fd5d236c0cebc0835d1 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:41:56 +0300 Subject: [PATCH 074/190] pylint: disable broad-exception-caught in code Disable all cases of W0718 broad-exception-caught in code. They are re-raised later either way. Part of #270 --- tarantool/connection.py | 6 +++--- tarantool/connection_pool.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 5369319e..f86da9d8 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1000,7 +1000,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=self.ssl_password) return - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught exc_list.append(exc) @@ -1012,7 +1012,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=line.rstrip()) return - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught exc_list.append(exc) @@ -1026,7 +1026,7 @@ def password_raise_error(): password=password_raise_error) return - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught exc_list.append(exc) raise SslError(exc_list) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 6b701587..60e7b7a6 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -638,7 +638,7 @@ def _request_process_loop(self, key, unit, last_refresh): method = getattr(Connection, task.method_name) try: resp = method(unit.conn, *task.args, **task.kwargs) - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught unit.output_queue.put(exc) else: unit.output_queue.put(resp) From c9682052c5e77babf92e44133d974915a2a540f7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:43:20 +0300 Subject: [PATCH 075/190] pylint: disable broad-exception-caught in tests Disable all cases of W0718 broad-exception-caught in test wrappers. Part of #270 --- test/suites/test_encoding.py | 2 +- test/suites/test_pool.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index d2e0568b..cbaf0bda 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -73,7 +73,7 @@ def setUpClass(cls): def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught self.fail(f'Function raised Exception: {repr(exc)}') def setUp(self): diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 50bca042..934fdcd6 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -69,7 +69,7 @@ def retry(self, func, count=5, timeout=0.5): for i in range(count): try: func() - except Exception as exc: + except Exception as exc: # pylint: disable=broad-exception-caught if i + 1 == count: raise exc From 269ed6f8e14e8d02d1862cf495546f200872f578 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:44:36 +0300 Subject: [PATCH 076/190] pylint: fix bare-except case Fix the only case of W0702 bare-except. Part of #270 --- test/suites/lib/skip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index d3b08fc8..15a82434 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -24,7 +24,7 @@ def fetch_tarantool_version(self): try: self.tnt_version = srv.admin.tnt_version - except: + except AttributeError: self.__class__.tnt_version = srv.admin.tnt_version def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): From 95a7eecbef91e1bc0158915a1a67fe6e36442247 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:47:14 +0300 Subject: [PATCH 077/190] pylint: fix broad-exception-raised cases Fix all cases of W0719 broad-exception-raised. Part of #270 --- tarantool/msgpack_ext/types/timezones/validate_timezones.py | 2 +- tarantool/utils.py | 6 +++--- test/suites/lib/tarantool_server.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tarantool/msgpack_ext/types/timezones/validate_timezones.py b/tarantool/msgpack_ext/types/timezones/validate_timezones.py index 943437f8..f2f00d69 100644 --- a/tarantool/msgpack_ext/types/timezones/validate_timezones.py +++ b/tarantool/msgpack_ext/types/timezones/validate_timezones.py @@ -14,4 +14,4 @@ continue if not timezone in timezoneAbbrevInfo: - raise Exception(f'Unknown Tarantool timezone {timezone}') + raise KeyError(f'Unknown Tarantool timezone {timezone}') diff --git a/tarantool/utils.py b/tarantool/utils.py index c10de9c2..775a827a 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -116,7 +116,7 @@ def greeting_decode(greeting_buf): try: (product, _, tail) = greeting_buf[0:63].decode().partition(' ') if product.startswith("Tarantool "): - raise Exception() + raise ValueError() # Parse a version string - 1.6.6-83-gc6b2129 or 1.6.7 (version, _, tail) = tail.partition(' ') version = version.split('-')[0].split('.') @@ -136,9 +136,9 @@ def greeting_decode(greeting_buf): # Tarantool < 1.6.7 doesn't add "(Binary)" to greeting result.protocol = "Binary" elif len(tail.strip()) != 0: - raise Exception("x") # Unsupported greeting + raise ValueError("x") # Unsupported greeting result.salt = base64_decode(greeting_buf[64:])[:20] return result - except Exception as exc: + except ValueError as exc: print('exx', exc) raise ValueError("Invalid greeting: " + str(greeting_buf)) from exc diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 6c2d3946..c144226d 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -312,7 +312,7 @@ def wait_until_started(self): elif ans in ('loading',): continue else: - raise Exception(f"Strange output for `box.info.status`: {ans}") + raise ValueError(f"Strange output for `box.info.status`: {ans}") except socket.error as exc: if exc.errno == errno.ECONNREFUSED: time.sleep(0.1) From 46633c053147e4cbec844fbdda362b3a0d144555 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:50:51 +0300 Subject: [PATCH 078/190] pylint: fix useless-object-inheritance cases Fix all cases of R0205 useless-object-inheritance. Part of #270 --- tarantool/crud.py | 2 +- tarantool/mesh_connection.py | 2 +- tarantool/request.py | 2 +- tarantool/schema.py | 6 +++--- tarantool/space.py | 2 +- test/suites/lib/remote_tarantool_server.py | 2 +- test/suites/lib/tarantool_admin.py | 2 +- test/suites/lib/tarantool_server.py | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tarantool/crud.py b/tarantool/crud.py index 8173847e..60858554 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -7,7 +7,7 @@ from tarantool.error import DatabaseError, ER_NO_SUCH_PROC, ER_ACCESS_DENIED -class CrudResponse(object): +class CrudResponse(): """ Contains response fields from the `crud`_ module that correspond to the Lua implementation. diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 0540293a..0e3dfc0b 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -199,7 +199,7 @@ def update_connection(conn, address): conn.auth_type = address['auth_type'] -class RoundRobinStrategy(object): +class RoundRobinStrategy(): """ Defines strategy to choose next pool server after fail. """ diff --git a/tarantool/request.py b/tarantool/request.py index a04f4a28..ec1c1e2f 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -122,7 +122,7 @@ def packer_factory(conn): return msgpack.Packer(**packer_kwargs) -class Request(object): +class Request(): """ Represents a single request to the server in compliance with the Tarantool protocol. Responsible for data encapsulation and building diff --git a/tarantool/schema.py b/tarantool/schema.py index 11992363..ae90d8a6 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -73,7 +73,7 @@ def to_unicode_recursive(value, max_depth): return to_unicode(value) -class SchemaIndex(object): +class SchemaIndex(): """ Contains schema for a space index. """ @@ -126,7 +126,7 @@ def flush(self): del self.space.indexes[self.name] -class SchemaSpace(object): +class SchemaSpace(): """ Contains schema for a space. """ @@ -171,7 +171,7 @@ def flush(self): del self.schema[self.name] -class Schema(object): +class Schema(): """ Contains Tarantool server spaces schema. """ diff --git a/tarantool/space.py b/tarantool/space.py index e9198ae9..11a3e3ca 100644 --- a/tarantool/space.py +++ b/tarantool/space.py @@ -4,7 +4,7 @@ """ -class Space(object): +class Space(): """ Object-oriented wrapper for accessing a particular space. Encapsulates the identifier of the space and provides a more diff --git a/test/suites/lib/remote_tarantool_server.py b/test/suites/lib/remote_tarantool_server.py index 0b3cb0c1..7d4118ba 100644 --- a/test/suites/lib/remote_tarantool_server.py +++ b/test/suites/lib/remote_tarantool_server.py @@ -26,7 +26,7 @@ def get_random_string(): return ''.join(random.choice(string.ascii_lowercase) for _ in range(16)) -class RemoteTarantoolServer(object): +class RemoteTarantoolServer(): """ Class to work with remote Tarantool server. """ diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index 04406792..67e49fbf 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -9,7 +9,7 @@ import yaml -class TarantoolAdmin(object): +class TarantoolAdmin(): """ Class to setup running Tarantool server. """ diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index c144226d..5fdcb4ce 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -51,7 +51,7 @@ def find_port(port=None): return find_port(3300) -class TarantoolServer(object): +class TarantoolServer(): """ Class to start up a new Tarantool server. """ From 26883ac137f2c2540cca96bc7a0b2641fcbc784f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 11:54:32 +0300 Subject: [PATCH 079/190] pylint: disable too-few-public-methods cases Disable all cases of R0903 too-few-public-methods. We define simple dataclasses, let's not rework them yet. Part of #270 --- tarantool/crud.py | 5 +++-- tarantool/schema.py | 3 +++ test/suites/test_ssl.py | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tarantool/crud.py b/tarantool/crud.py index 60858554..a8816049 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -14,6 +14,7 @@ class CrudResponse(): .. _crud: https://github.com/tarantool/crud/ """ + # pylint: disable=too-few-public-methods def __init__(self, response): """ @@ -33,14 +34,14 @@ def __init__(self, response): raise RuntimeError('Unable to decode response to object due to unknown type') -class CrudResult(CrudResponse): +class CrudResult(CrudResponse): # pylint: disable=too-few-public-methods """ Contains result's fields from result variable of crud module operation. """ -class CrudError(CrudResponse): +class CrudError(CrudResponse): # pylint: disable=too-few-public-methods """ Contains error's fields from error variable of crud module operation. diff --git a/tarantool/schema.py b/tarantool/schema.py index ae90d8a6..3d333435 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -77,6 +77,7 @@ class SchemaIndex(): """ Contains schema for a space index. """ + # pylint: disable=too-few-public-methods def __init__(self, index_row, space): """ @@ -130,6 +131,7 @@ class SchemaSpace(): """ Contains schema for a space. """ + # pylint: disable=too-few-public-methods def __init__(self, space_row, schema): """ @@ -175,6 +177,7 @@ class Schema(): """ Contains Tarantool server spaces schema. """ + # pylint: disable=too-few-public-methods def __init__(self, con): """ diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 2cf64e5f..ce1d8cc0 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -34,6 +34,8 @@ def is_test_ssl(): class SslTestCase: + # pylint: disable=too-few-public-methods + def __init__(self, name="", ok=False, From eac7c9cfd74f40eb8eec043056138282b83f1d68 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:00:19 +0300 Subject: [PATCH 080/190] pylint: disable too-many-instance-attributes cases Disable all cases of R0902 too-many-instance-attributes. We do not plan to change existing API now. Part of #270 --- tarantool/connection.py | 1 + tarantool/msgpack_ext/types/interval.py | 1 + tarantool/response.py | 1 + tarantool/types.py | 1 + test/suites/lib/tarantool_server.py | 1 + test/suites/test_mesh.py | 2 ++ test/suites/test_ssl.py | 2 +- 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index f86da9d8..020d4ca9 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -488,6 +488,7 @@ class Connection(ConnectionInterface): check its status, call procedures and evaluate Lua code on server, make simple data manipulations and execute SQL queries. """ + # pylint: disable=too-many-instance-attributes # DBAPI Extension: supply exceptions as attributes on the connection Error = Error diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index 66679b92..f9098a4b 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -57,6 +57,7 @@ class Interval(): .. _datetime.interval: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-interval-type """ + # pylint: disable=too-many-instance-attributes def __init__(self, *, year=0, month=0, week=0, day=0, hour=0, minute=0, sec=0, diff --git a/tarantool/response.py b/tarantool/response.py index ee06ca58..99e23865 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -92,6 +92,7 @@ class Response(Sequence): received list of tuples) and parsing of binary packets received from the server. """ + # pylint: disable=too-many-instance-attributes def __init__(self, conn, response): """ diff --git a/tarantool/types.py b/tarantool/types.py index 5ca86150..d15262ac 100644 --- a/tarantool/types.py +++ b/tarantool/types.py @@ -13,6 +13,7 @@ class BoxError(): .. _box.error: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_error/error/ """ + # pylint: disable=too-many-instance-attributes type: typing.Union[str, bytes] """ diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 5fdcb4ce..7d7c666e 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -55,6 +55,7 @@ class TarantoolServer(): """ Class to start up a new Tarantool server. """ + # pylint: disable=too-many-instance-attributes default_tarantool = { "bin": "tarantool", diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index c5cdc49d..e7bf58ff 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -34,6 +34,8 @@ def create_server(_id): @unittest.skipIf(sys.platform.startswith("win"), 'Mesh tests on windows platform are not supported') class TestSuiteMesh(unittest.TestCase): + # pylint: disable=too-many-instance-attributes + def define_cluster_function(self, func_name, servers): addresses = [(srv.host, srv.args['primary']) for srv in servers] addresses_lua = ",".join(f"'{address[0]}:{address[1]}'" for address in addresses) diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index ce1d8cc0..9c9ad6bb 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -34,7 +34,7 @@ def is_test_ssl(): class SslTestCase: - # pylint: disable=too-few-public-methods + # pylint: disable=too-few-public-methods,too-many-instance-attributes def __init__(self, name="", From c705deffe22197c0849c16800b9481ca9835177a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:03:37 +0300 Subject: [PATCH 081/190] pylint: disable too-many-public-methods cases Disable all cases of R0904 too-many-public-methods. We do not plan to change existing API now. Part of #270 --- tarantool/connection.py | 3 ++- tarantool/connection_pool.py | 1 + test/suites/test_datetime.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_schema.py | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 020d4ca9..6aeb3eec 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -122,6 +122,7 @@ class ConnectionInterface(metaclass=abc.ABCMeta): Lua code on server, make simple data manipulations and execute SQL queries. """ + # pylint: disable=too-many-public-methods @classmethod def __subclasshook__(cls, subclass): @@ -488,7 +489,7 @@ class Connection(ConnectionInterface): check its status, call procedures and evaluate Lua code on server, make simple data manipulations and execute SQL queries. """ - # pylint: disable=too-many-instance-attributes + # pylint: disable=too-many-instance-attributes,too-many-public-methods # DBAPI Extension: supply exceptions as attributes on the connection Error = Error diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 60e7b7a6..cb30c7d6 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -364,6 +364,7 @@ class ConnectionPool(ConnectionInterface): >>> resp - ['AAAA', 'Alpha'] """ + # pylint: disable=too-many-public-methods def __init__(self, addrs, diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index d38fe1f1..34ebb696 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,7 +1,7 @@ """ This module tests work with datetime type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods import sys import re diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 6dcb732d..5025c140 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,7 +1,7 @@ """ This module tests basic data operations. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme,too-many-public-methods import sys import unittest diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 934fdcd6..2b3792ef 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods import sys import time diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index bf5c79b4..a13ddb1e 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,fixme +# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods import sys import unittest From 6d8cbc4297906426cc3767c27f10d03a1783e07d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:14:46 +0300 Subject: [PATCH 082/190] pylint: fix too-many-return-statements case Fix a case of R0911 too-many-return-statements by reworking pool code. Part of #270 --- tarantool/connection_pool.py | 52 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index cb30c7d6..9ad4cdde 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -270,6 +270,27 @@ def update(self): self.rebuild_needed = True + def _getnext_by_mode(self, *iters, err_msg="Can't find healthy instance in pool"): + """ + Get server from prioritized list of iterators. + + :param iters: list of iterators + :type iters: :obj:`list` + + :param err_msg: Error message to raise in case of error. + :type err_msg: :obj:`str` + + :rtype: :class:`~tarantool.connection_pool.PoolUnit` + + :raise: :exc:`~tarantool.error.PoolTolopogyError` + + :meta private: + """ + for itr in iters: + if itr is not None: + return next(itr) + raise PoolTolopogyError(err_msg) + def getnext(self, mode): """ Get server based on the request mode. @@ -286,34 +307,17 @@ def getnext(self, mode): self.build() if mode == Mode.ANY: - if self.any_iter is not None: - return next(self.any_iter) - else: - raise PoolTolopogyError("Can't find healthy instance in pool") + return self._getnext_by_mode(self.any_iter) elif mode == Mode.RW: - if self.rw_iter is not None: - return next(self.rw_iter) - else: - raise PoolTolopogyError("Can't find healthy rw instance in pool") + return self._getnext_by_mode(self.rw_iter, + err_msg="Can't find healthy rw instance in pool") elif mode == Mode.RO: - if self.ro_iter is not None: - return next(self.ro_iter) - else: - raise PoolTolopogyError("Can't find healthy ro instance in pool") + return self._getnext_by_mode(self.ro_iter, + err_msg="Can't find healthy ro instance in pool") elif mode == Mode.PREFER_RO: - if self.ro_iter is not None: - return next(self.ro_iter) - elif self.rw_iter is not None: - return next(self.rw_iter) - else: - raise PoolTolopogyError("Can't find healthy instance in pool") + return self._getnext_by_mode(self.ro_iter, self.rw_iter) elif mode == Mode.PREFER_RW: - if self.rw_iter is not None: - return next(self.rw_iter) - elif self.ro_iter is not None: - return next(self.ro_iter) - else: - raise PoolTolopogyError("Can't find healthy instance in pool") + return self._getnext_by_mode(self.rw_iter, self.ro_iter) @dataclass From e0ee0cacfb054b19b24efb672503b1561ea6e734 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:16:34 +0300 Subject: [PATCH 083/190] pylint: disable too-many-return-statements case Disable the only remaining case of R0911 too-many-return-statements. It's not worth it to rework deprecated code. Part of #270 --- tarantool/mesh_connection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 0e3dfc0b..0403763e 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -118,6 +118,7 @@ def prepare_address(address): :rtype: first value: :obj:`dict` or ``None``, second value: ``None`` or :obj:`str` """ + # pylint: disable=too-many-return-statements def format_error(address, err): return None, f'Address {str(address)}: {err}' From 9baa4580c012ab2f2a7f86fd395a6901bd684ac5 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:29:35 +0300 Subject: [PATCH 084/190] pylint: disable too-many-branches cases Disable cases of R0912 too-many-branches. We don't plan to change existing behavior in datetime; it's not worth it to rework deprecated code; branches in tests are required to build subtests. Part of #270 --- tarantool/mesh_connection.py | 3 ++- tarantool/msgpack_ext/types/datetime.py | 1 + test/suites/test_schema.py | 2 +- test/suites/test_ssl.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 0403763e..549d3138 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -59,6 +59,7 @@ def parse_uri(uri): :rtype: first value: :obj:`dict` or ``None``, second value: ``None`` or :obj:`str` """ + # pylint: disable=too-many-branches # TODO: Support Unix sockets. def parse_error(uri, msg): @@ -118,7 +119,7 @@ def prepare_address(address): :rtype: first value: :obj:`dict` or ``None``, second value: ``None`` or :obj:`str` """ - # pylint: disable=too-many-return-statements + # pylint: disable=too-many-return-statements,too-many-branches def format_error(address, err): return None, f'Address {str(address)}: {err}' diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index ac191dbb..1ab5663b 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -261,6 +261,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, .. _datetime.new(): https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ """ + # pylint: disable=too-many-branches tzinfo = None if tz != '': diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index a13ddb1e..0a26f170 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods +# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods,too-many-branches import sys import unittest diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 9c9ad6bb..dc8e218e 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,7 +1,7 @@ """ This module tests connection through SSL. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches import os import sys From 41a365d81e7f3617bf5bee51a272216fa0ab1014 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:32:36 +0300 Subject: [PATCH 085/190] pylint: disable too-many-arguments cases Disable all cases of R0913 too-many-arguments. We don't plan to change existing public API now. Part of #270 --- tarantool/__init__.py | 1 + tarantool/connection.py | 1 + tarantool/connection_pool.py | 1 + tarantool/mesh_connection.py | 1 + tarantool/request.py | 4 ++++ test/suites/lib/tarantool_server.py | 2 +- test/suites/test_ssl.py | 2 +- 7 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 499b1055..277c138d 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -1,6 +1,7 @@ """ This package provides API for interaction with a Tarantool server. """ +# pylint: disable=too-many-arguments import sys diff --git a/tarantool/connection.py b/tarantool/connection.py index 6aeb3eec..955a6ab5 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -788,6 +788,7 @@ def __init__(self, host, port, .. _mp_bin: https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family .. _mp_array: https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family """ + # pylint: disable=too-many-arguments if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'): raise ConfigurationError("msgpack>=1.0.0 only supports None and " + diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 9ad4cdde..be0b71d6 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -467,6 +467,7 @@ def __init__(self, .. _box.info.status: .. _box.info: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_info/ """ + # pylint: disable=too-many-arguments if not isinstance(addrs, list) or len(addrs) == 0: raise ConfigurationError("addrs must be non-empty list") diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 549d3138..66834895 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -437,6 +437,7 @@ def __init__(self, host=None, port=None, :class:`~tarantool.Connection` exceptions, :class:`~tarantool.MeshConnection.connect` exceptions """ + # pylint: disable=too-many-arguments if addrs is None: addrs = [] diff --git a/tarantool/request.py b/tarantool/request.py index ec1c1e2f..3d1ef974 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -271,6 +271,7 @@ def __init__(self, conn, salt, user, password, auth_type=AUTH_TYPE_CHAP_SHA1): :param auth_type: Refer to :paramref:`~tarantool.Connection.auth_type`. :type auth_type: :obj:`str`, optional """ + # pylint: disable=too-many-arguments super(RequestAuthenticate, self).__init__(conn) @@ -406,6 +407,7 @@ def __init__(self, conn, space_no, index_no, key, offset, limit, iterator): :raise: :exc:`~AssertionError` """ + # pylint: disable=too-many-arguments super(RequestSelect, self).__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, @@ -446,6 +448,7 @@ def __init__(self, conn, space_no, index_no, key, op_list): :raise: :exc:`~AssertionError` """ + # pylint: disable=too-many-arguments super(RequestUpdate, self).__init__(conn) @@ -568,6 +571,7 @@ def __init__(self, conn, space_no, index_no, tuple_value, op_list): :raise: :exc:`~AssertionError` """ + # pylint: disable=too-many-arguments super(RequestUpsert, self).__init__(conn) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 7d7c666e..2480cebb 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -55,7 +55,7 @@ class TarantoolServer(): """ Class to start up a new Tarantool server. """ - # pylint: disable=too-many-instance-attributes + # pylint: disable=too-many-instance-attributes,too-many-arguments default_tarantool = { "bin": "tarantool", diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index dc8e218e..d6fa484a 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -34,7 +34,7 @@ def is_test_ssl(): class SslTestCase: - # pylint: disable=too-few-public-methods,too-many-instance-attributes + # pylint: disable=too-few-public-methods,too-many-instance-attributes,too-many-arguments def __init__(self, name="", From a2166f158c2d8ed9efb97cc77ae7707a9f8e3729 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:36:26 +0300 Subject: [PATCH 086/190] pylint: disable too-many-locals cases Disable cases of R0914 too-many-locals. It seems to have the same reasons as too-many-arguments. Part of #270 --- tarantool/connection.py | 2 +- tarantool/connection_pool.py | 2 +- tarantool/mesh_connection.py | 2 +- tarantool/msgpack_ext/types/datetime.py | 2 +- test/suites/test_ssl.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 955a6ab5..bc0bd889 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -788,7 +788,7 @@ def __init__(self, host, port, .. _mp_bin: https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family .. _mp_array: https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-locals if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'): raise ConfigurationError("msgpack>=1.0.0 only supports None and " + diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index be0b71d6..3b3a7086 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -467,7 +467,7 @@ def __init__(self, .. _box.info.status: .. _box.info: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_info/ """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-locals if not isinstance(addrs, list) or len(addrs) == 0: raise ConfigurationError("addrs must be non-empty list") diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 66834895..ac6334b1 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -437,7 +437,7 @@ def __init__(self, host=None, port=None, :class:`~tarantool.Connection` exceptions, :class:`~tarantool.MeshConnection.connect` exceptions """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-locals if addrs is None: addrs = [] diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 1ab5663b..0860be0a 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -261,7 +261,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, .. _datetime.new(): https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ """ - # pylint: disable=too-many-branches + # pylint: disable=too-many-branches,too-many-locals tzinfo = None if tz != '': diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index d6fa484a..8f989d7d 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,7 +1,7 @@ """ This module tests connection through SSL. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches,too-many-locals import os import sys From 5b72967a3a58797c0e091bdfc1dc0f3bf744247e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:37:35 +0300 Subject: [PATCH 087/190] pylint: disable too-many-statements case in test Disable the only case of R0915 too-many-statements in test. Part of #270 --- test/suites/test_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 0a26f170..7e89b130 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods,too-many-branches +# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods,too-many-branches,too-many-statements import sys import unittest From b631e9e9254f929bd035d4285fa5859c21297c40 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:39:12 +0300 Subject: [PATCH 088/190] pylint: disable too-many-boolean-expressions case Disable the only case of R0916 too-many-boolean-expressions in code. It doesn't seem to have an obvious rework with the same behavior which will make it better. Part of #270 --- tarantool/msgpack_ext/types/datetime.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 0860be0a..e214d987 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -276,6 +276,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, # The logic is same as in Tarantool, refer to datetime API. # https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ if timestamp is not None: + # pylint: disable=too-many-boolean-expressions if ((year is not None) or (month is not None) or \ (day is not None) or (hour is not None) or \ (minute is not None) or (sec is not None)): From 358c291a3060a858f82f6e0e2b2fcbc0704d650a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:45:42 +0300 Subject: [PATCH 089/190] pylint: fix consider-using-from-import case Fix the only case of R0801 consider-using-from-import. Part of #270 --- tarantool/schema.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index 3d333435..5db928e2 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -7,7 +7,16 @@ SchemaError, DatabaseError ) -import tarantool.const as const +from tarantool.const import ( + INDEX_SPACE_NAME, + INDEX_INDEX_NAME, + INDEX_SPACE_PRIMARY, + INDEX_INDEX_PRIMARY, + SPACE_VSPACE, + SPACE_VINDEX, + SPACE_SPACE, + SPACE_INDEX +) MAX_RECURSION_DEPTH = 32 """ @@ -257,9 +266,9 @@ def fetch_space_from(self, space): _index = None if isinstance(space, str): - _index = const.INDEX_SPACE_NAME + _index = INDEX_SPACE_NAME else: - _index = const.INDEX_SPACE_PRIMARY + _index = INDEX_SPACE_PRIMARY if space is None: space = () @@ -267,7 +276,7 @@ def fetch_space_from(self, space): space_row = None try: # Try to fetch from '_vspace' - space_row = self.con.select(const.SPACE_VSPACE, space, + space_row = self.con.select(SPACE_VSPACE, space, index=_index) except DatabaseError as exc: # if space can't be found, then user is using old version of @@ -276,7 +285,7 @@ def fetch_space_from(self, space): raise if space_row is None: # Try to fetch from '_space' - space_row = self.con.select(const.SPACE_SPACE, space, index=_index) + space_row = self.con.select(SPACE_SPACE, space, index=_index) return space_row @@ -386,9 +395,9 @@ def fetch_index_from(self, space, index): _index = None if isinstance(index, str): - _index = const.INDEX_INDEX_NAME + _index = INDEX_INDEX_NAME else: - _index = const.INDEX_INDEX_PRIMARY + _index = INDEX_INDEX_PRIMARY _key_tuple = None if space is None and index is None: @@ -403,7 +412,7 @@ def fetch_index_from(self, space, index): index_row = None try: # Try to fetch from '_vindex' - index_row = self.con.select(const.SPACE_VINDEX, _key_tuple, + index_row = self.con.select(SPACE_VINDEX, _key_tuple, index=_index) except DatabaseError as exc: # if space can't be found, then user is using old version of @@ -412,7 +421,7 @@ def fetch_index_from(self, space, index): raise if index_row is None: # Try to fetch from '_index' - index_row = self.con.select(const.SPACE_INDEX, _key_tuple, + index_row = self.con.select(SPACE_INDEX, _key_tuple, index=_index) return index_row From 277db60ca8b4016aee1b8b4b79db9a8140b34fa5 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 12:51:27 +0300 Subject: [PATCH 090/190] pylint: fix access-member-before-definition case Fix the only case of E0203 access-member-before-definition. Part of #270 --- tarantool/mesh_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index ac6334b1..cad602b3 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -213,6 +213,7 @@ def __init__(self, addrs): :type addrs: :obj:`list` of :obj:`dict` """ self.pos = None + self.addrs = [] self.update(addrs) def update(self, new_addrs): @@ -234,7 +235,7 @@ def update(self, new_addrs): new_addrs = new_addrs_unique # Save a current address if any. - if 'pos' in self.__dict__ and 'addrs' in self.__dict__: + if self.pos is not None: current_addr = self.addrs[self.pos] else: current_addr = None From 5aa8ba40bc173d3a3e4f823163595d99cab3e43f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 13:03:48 +0300 Subject: [PATCH 091/190] pylint: fix function-redefined case Fix all cases of E0102 function-redefined. Part of #270 --- test/suites/test_datetime.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 34ebb696..80a43b73 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -482,25 +482,25 @@ def test_tarantool_datetime_subtraction_different_timezones(self): }, } - def test_python_datetime_addition(self): + def test_python_interval_addition(self): for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): self.assertEqual(case['arg_1'] + case['arg_2'], case['res_add']) - def test_python_datetime_subtraction(self): + def test_python_interval_subtraction(self): for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): self.assertEqual(case['arg_1'] - case['arg_2'], case['res_sub']) @skip_or_run_datetime_test - def test_tarantool_datetime_addition(self): + def test_tarantool_interval_addition(self): for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): self.assertSequenceEqual(self.con.call('add', case['arg_1'], case['arg_2']), [case['res_add']]) @skip_or_run_datetime_test - def test_tarantool_datetime_subtraction(self): + def test_tarantool_interval_subtraction(self): for name, case in self.interval_arithmetic_cases.items(): with self.subTest(msg=name): self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), From adf6ed5642966233611195bd1b756680ce22b215 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 14:23:45 +0300 Subject: [PATCH 092/190] pylint: fix inconsistent-return-statements cases Fix all cases of R1710 inconsistent-return-statements. Part of #270 --- tarantool/connection.py | 4 +++- tarantool/connection_pool.py | 2 ++ test/suites/lib/tarantool_admin.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index bc0bd889..2320208d 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1196,7 +1196,8 @@ def _opt_reconnect(self): # **Bug in Python: timeout is an internal Python construction (???). if not self._socket: - return self.connect() + self.connect() + return def check(): # Check that connection is alive buf = ctypes.create_string_buffer(2) @@ -1205,6 +1206,7 @@ def check(): # Check that connection is alive except socket.error as exc: if exc.errno == errno.EBADF: return errno.ECONNRESET + return exc.errno else: if os.name == 'nt': flag = socket.MSG_PEEK diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 3b3a7086..6a44c13d 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -319,6 +319,8 @@ def getnext(self, mode): elif mode == Mode.PREFER_RW: return self._getnext_by_mode(self.rw_iter, self.ro_iter) + raise ValueError(f"Unexpected mode {mode}") + @dataclass class PoolTask(): diff --git a/test/suites/lib/tarantool_admin.py b/test/suites/lib/tarantool_admin.py index 67e49fbf..de75a0e2 100644 --- a/test/suites/lib/tarantool_admin.py +++ b/test/suites/lib/tarantool_admin.py @@ -65,7 +65,7 @@ def execute(self, command): """ if not command: - return + return None if not self.is_connected: self.connect() From 34c87cbb78998edf7b6e035212311647e2a7b151 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 14:33:27 +0300 Subject: [PATCH 093/190] internal: move validate script to bash validate_timezones is a prt of gen-timezones script, but it is easy to treat it as a part of the package (pylint do that too). Part of #270 --- .../types/timezones/gen-timezones.sh | 15 ++++++++++++++- .../types/timezones/validate_timezones.py | 17 ----------------- 2 files changed, 14 insertions(+), 18 deletions(-) delete mode 100644 tarantool/msgpack_ext/types/timezones/validate_timezones.py diff --git a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh index d87868d9..161a7597 100755 --- a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh +++ b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh @@ -70,4 +70,17 @@ echo "}" >> ${DST_FILE} rm timezones.h -python validate_timezones.py +python < Date: Wed, 29 Mar 2023 14:40:45 +0300 Subject: [PATCH 094/190] pylint: fix inconsistent-return-statements cases Fix all cases of C1802 use-implicit-booleaness-not-len. Part of #270 --- tarantool/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index 5db928e2..9395f13a 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -240,7 +240,7 @@ def fetch_space(self, space): raise SchemaError( 'Some strange output from server: \n' + str(space_row) ) - elif len(space_row) == 0 or not len(space_row[0]): + elif len(space_row) == 0 or not space_row[0]: # We can't find space with this name or id temp_name = 'name' if isinstance(space, str) else 'id' errmsg = f"There's no space with {temp_name} '{space}'" @@ -352,7 +352,7 @@ def fetch_index(self, space_object, index): raise SchemaError( 'Some strange output from server: \n' + str(index_row) ) - elif len(index_row) == 0 or not len(index_row[0]): + elif len(index_row) == 0 or not index_row[0]: # We can't find index with this name or id temp_name = 'name' if isinstance(index, str) else 'id' errmsg = (f"There's no index with {temp_name} '{index}'" From 4eed0601fa009f07af22d7f4c6ca2d0f40261e1a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 14:44:23 +0300 Subject: [PATCH 095/190] pylint: fix consider-using-in cases Fix all cases of R1714 consider-using-in. Part of #270 --- tarantool/connection.py | 4 +--- tarantool/crud.py | 2 +- tarantool/msgpack_ext/decimal.py | 4 ++-- test/suites/test_package.py | 2 +- test/suites/test_ssl.py | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 2320208d..337bba54 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1222,9 +1222,7 @@ def check(): # Check that connection is alive err = ctypes.get_last_error() self._socket.setblocking(True) - if (retbytes < 0) and (err == errno.EAGAIN or - err == errno.EWOULDBLOCK or - err == WWSAEWOULDBLOCK): + if (retbytes < 0) and err in (errno.EAGAIN, errno.EWOULDBLOCK, WWSAEWOULDBLOCK): ctypes.set_errno(0) return errno.EAGAIN else: diff --git a/tarantool/crud.py b/tarantool/crud.py index a8816049..7850d164 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -66,7 +66,7 @@ def call_crud(conn, *args): try: crud_resp = conn.call(*args) except DatabaseError as exc: - if exc.code == ER_NO_SUCH_PROC or exc.code == ER_ACCESS_DENIED: + if exc.code in (ER_NO_SUCH_PROC, ER_ACCESS_DENIED): exc_msg = ". Ensure that you're calling crud.router and user has sufficient grants" raise DatabaseError(exc.code, exc.message + exc_msg, extra_info=exc.extra_info) from exc diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index 04e14c7f..c53cd73b 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -300,10 +300,10 @@ def get_str_sign(nibble): :meta private: """ - if nibble == 0x0a or nibble == 0x0c or nibble == 0x0e or nibble == 0x0f: + if nibble in (0x0a, 0x0c, 0x0e, 0x0f): return '+' - if nibble == 0x0b or nibble == 0x0d: + if nibble in (0x0b, 0x0d): return '-' raise MsgpackError('Unexpected MP_DECIMAL sign nibble') diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 24be800f..7799d4fe 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -19,7 +19,7 @@ def is_test_pure_install(): env = os.getenv("TEST_PURE_INSTALL") if env: env = env.upper() - return env == "1" or env == "TRUE" + return env in ("1", "TRUE") return False diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 8f989d7d..99707f6a 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -29,7 +29,7 @@ def is_test_ssl(): env = os.getenv("TEST_TNT_SSL") if env: env = env.upper() - return env == "1" or env == "TRUE" + return env in ("1", "TRUE") return False From f4dfd43e6f0216aa1dc67fb17cdc6674d29ae237 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:21:10 +0300 Subject: [PATCH 096/190] pylint: fix use-dict-literal cases Fix all cases of R1735 use-dict-literal. Part of #270 --- tarantool/request.py | 2 +- tarantool/response.py | 2 +- tarantool/schema.py | 4 ++-- test/suites/test_dbapi.py | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tarantool/request.py b/tarantool/request.py index 3d1ef974..26220781 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -71,7 +71,7 @@ def packer_factory(conn): :rtype: :class:`msgpack.Packer` """ - packer_kwargs = dict() + packer_kwargs = {} # use_bin_type=True is default since msgpack-1.0.0. # diff --git a/tarantool/response.py b/tarantool/response.py index 99e23865..f98fc6e7 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -43,7 +43,7 @@ def unpacker_factory(conn): :rtype: :class:`msgpack.Unpacker` """ - unpacker_kwargs = dict() + unpacker_kwargs = {} # Decode MsgPack arrays into Python lists by default (not tuples). # Can be configured in the Connection init diff --git a/tarantool/schema.py b/tarantool/schema.py index 9395f13a..97ce8a48 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -63,7 +63,7 @@ def to_unicode_recursive(value, max_depth): raise RecursionError('Max recursion depth is reached') if isinstance(value, dict): - res = dict() + res = {} for key, val in value.items(): key = to_unicode_recursive(key, max_depth - 1) val = to_unicode_recursive(val, max_depth - 1) @@ -161,7 +161,7 @@ def __init__(self, space_row, schema): self.schema[self.sid] = self if self.name: self.schema[self.name] = self - self.format = dict() + self.format = {} try: format_raw = to_unicode_recursive(space_row[6], MAX_RECURSION_DEPTH) except RecursionError as exc: diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index da104c03..5968f648 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -32,9 +32,10 @@ def setUpClass(cls): cls.srv.start() cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) cls.driver = dbapi - cls.connect_kw_args = dict( - host=cls.srv.host, - port=cls.srv.args['primary']) + cls.connect_kw_args = { + "host": cls.srv.host, + "port": cls.srv.args['primary'] + } @skip_or_run_sql_test def setUp(self): From 791a9dfd530d9cdcb94f62546160d17e2cd9fc78 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:30:17 +0300 Subject: [PATCH 097/190] pylint: fix super-with-arguments cases Fix all cases of R1725 super-with-arguments. Part of #270 --- tarantool/dbapi.py | 4 ++-- tarantool/error.py | 19 +++++++++---------- tarantool/mesh_connection.py | 8 ++++---- tarantool/request.py | 30 +++++++++++++++--------------- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 334673f1..e94a45de 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -295,7 +295,7 @@ def __init__(self, *args, **kwargs): :raise: :class:`~tarantool.Connection` exceptions """ - super(Connection, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._set_autocommit(kwargs.get('autocommit', True)) def _set_autocommit(self, autocommit): @@ -354,7 +354,7 @@ def close(self): """ self._check_not_closed("The closed connector can not be closed again.") - super(Connection, self).close() + super().close() def commit(self): """ diff --git a/tarantool/error.py b/tarantool/error.py index 254205b7..35ca3007 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -203,7 +203,7 @@ def __init__(self, value): :param value: Error value. """ - super(SchemaError, self).__init__(0, value) + super().__init__(0, value) self.value = value def __str__(self): @@ -224,7 +224,7 @@ def __init__(self, message, schema_version): :type schema_version: :obj:`int` """ - super(SchemaReloadException, self).__init__(109, message) + super().__init__(109, message) self.schema_version = schema_version def __str__(self): @@ -251,13 +251,12 @@ def __init__(self, orig_exception=None, *args, **kwargs): if orig_exception: if isinstance(orig_exception, socket.timeout): self.message = "Socket timeout" - super(NetworkError, self).__init__(0, self.message) + super().__init__(0, self.message) elif isinstance(orig_exception, socket.error): self.message = os.strerror(orig_exception.errno) - super(NetworkError, self).__init__( - orig_exception.errno, self.message) + super().__init__(orig_exception.errno, self.message) else: - super(NetworkError, self).__init__(orig_exception, *args, **kwargs) + super().__init__(orig_exception, *args, **kwargs) class NetworkWarning(UserWarning): @@ -284,9 +283,9 @@ def __init__(self, orig_exception=None, *args): self.errno = orig_exception.errno if orig_exception: if IS_SSL_SUPPORTED and isinstance(orig_exception, ssl.SSLError): - super(SslError, self).__init__(orig_exception, *args) + super().__init__(orig_exception, *args) else: - super(SslError, self).__init__(orig_exception, *args) + super().__init__(orig_exception, *args) class ClusterDiscoveryWarning(UserWarning): @@ -329,7 +328,7 @@ def __init__(self, _, error): :type args: :obj:`tuple` """ - super(CrudModuleError, self).__init__(0, error.err) + super().__init__(0, error.err) # Sets tarantool.crud.CrudError object. self.extra_info_error = error @@ -351,7 +350,7 @@ def __init__(self, success, error): """ exc_msg = "Got multiple errors, see errors_list and success_list" - super(CrudModuleManyError, self).__init__(0, exc_msg) + super().__init__(0, exc_msg) # Sets list of tarantool.crud.CrudResult objects. self.success_list = success # Sets list of tarantool.crud.CrudError objects. diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index cad602b3..9d70b4ab 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -480,7 +480,7 @@ def __init__(self, host=None, port=None, self.cluster_discovery_delay = cluster_discovery_delay self.last_nodes_refresh = 0 - super(MeshConnection, self).__init__( + super().__init__( host=addr['host'], port=addr['port'], user=user, @@ -514,7 +514,7 @@ def connect(self): :exc:`~tarantool.error.NetworkError`, :class:`~tarantool.Connection.connect` exceptions """ - super(MeshConnection, self).connect() + super().connect() if self.connected and self.cluster_discovery_function: self._opt_refresh_instances() @@ -530,7 +530,7 @@ def _opt_reconnect(self): last_error = None for _ in range(len(self.strategy.addrs)): try: - super(MeshConnection, self)._opt_reconnect() + super()._opt_reconnect() last_error = None break except NetworkError as exc: @@ -632,4 +632,4 @@ def _send_request(self, request, on_push=None, on_push_ctx=None): """ self._opt_refresh_instances() - return super(MeshConnection, self)._send_request(request, on_push, on_push_ctx) + return super()._send_request(request, on_push, on_push_ctx) diff --git a/tarantool/request.py b/tarantool/request.py index 26220781..99b9d5f0 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -216,7 +216,7 @@ def __init__(self, conn, space_no, values): :raise: :exc:`~AssertionError` """ - super(RequestInsert, self).__init__(conn) + super().__init__(conn) assert isinstance(values, (tuple, list)) request_body = self._dumps({IPROTO_SPACE_ID: space_no, @@ -273,7 +273,7 @@ def __init__(self, conn, salt, user, password, auth_type=AUTH_TYPE_CHAP_SHA1): """ # pylint: disable=too-many-arguments - super(RequestAuthenticate, self).__init__(conn) + super().__init__(conn) if auth_type == AUTH_TYPE_CHAP_SHA1: hash1 = sha1((password,)) @@ -332,7 +332,7 @@ def __init__(self, conn, space_no, values): :raise: :exc:`~AssertionError` """ - super(RequestReplace, self).__init__(conn) + super().__init__(conn) assert isinstance(values, (tuple, list)) request_body = self._dumps({IPROTO_SPACE_ID: space_no, @@ -365,7 +365,7 @@ def __init__(self, conn, space_no, index_no, key): :raise: :exc:`~AssertionError` """ - super(RequestDelete, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, IPROTO_INDEX_ID: index_no, @@ -409,7 +409,7 @@ def __init__(self, conn, space_no, index_no, key, offset, limit, iterator): """ # pylint: disable=too-many-arguments - super(RequestSelect, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, IPROTO_INDEX_ID: index_no, IPROTO_OFFSET: offset, @@ -450,7 +450,7 @@ def __init__(self, conn, space_no, index_no, key, op_list): """ # pylint: disable=too-many-arguments - super(RequestUpdate, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, IPROTO_INDEX_ID: index_no, @@ -487,7 +487,7 @@ def __init__(self, conn, name, args, call_16): if call_16: self.request_type = REQUEST_TYPE_CALL16 - super(RequestCall, self).__init__(conn) + super().__init__(conn) assert isinstance(args, (list, tuple)) request_body = self._dumps({IPROTO_FUNCTION_NAME: name, @@ -517,7 +517,7 @@ def __init__(self, conn, name, args): :raise: :exc:`~AssertionError` """ - super(RequestEval, self).__init__(conn) + super().__init__(conn) assert isinstance(args, (list, tuple)) request_body = self._dumps({IPROTO_EXPR: name, @@ -539,7 +539,7 @@ def __init__(self, conn): :type conn: :class:`~tarantool.Connection` """ - super(RequestPing, self).__init__(conn) + super().__init__(conn) self._body = b'' @@ -573,7 +573,7 @@ def __init__(self, conn, space_no, index_no, tuple_value, op_list): """ # pylint: disable=too-many-arguments - super(RequestUpsert, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, IPROTO_INDEX_ID: index_no, @@ -599,7 +599,7 @@ def __init__(self, conn, server_uuid): :type server_uuid: :obj:`str` """ - super(RequestJoin, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_SERVER_UUID: server_uuid}) self._body = request_body @@ -628,7 +628,7 @@ def __init__(self, conn, cluster_uuid, server_uuid, vclock): :raise: :exc:`~AssertionError` """ - super(RequestSubscribe, self).__init__(conn) + super().__init__(conn) assert isinstance(vclock, dict) request_body = self._dumps({ @@ -655,7 +655,7 @@ def __init__(self, conn, sync): :type sync: :obj:`int` """ - super(RequestOK, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_REQUEST_TYPE: self.request_type, IPROTO_SYNC: sync}) self._body = request_body @@ -682,7 +682,7 @@ def __init__(self, conn, sql, args): :raise: :exc:`~TypeError` """ - super(RequestExecute, self).__init__(conn) + super().__init__(conn) if isinstance(args, Mapping): args = [{f":{name}": value} for name, value in args.items()] elif not isinstance(args, Sequence): @@ -715,7 +715,7 @@ def __init__(self, conn, protocol_version, features): :type features: :obj:`list` """ - super(RequestProtocolVersion, self).__init__(conn) + super().__init__(conn) request_body = self._dumps({IPROTO_VERSION: protocol_version, IPROTO_FEATURES: features}) From 657a3c5f7ab4070d1cd38c2926905209cb8b548c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:31:38 +0300 Subject: [PATCH 098/190] pylint: fix use-list-literal cases Fix all cases of R1734 use-list-literal. Part of #270 --- tarantool/connection.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 337bba54..2b875ee0 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -2298,7 +2298,7 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: Op res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) @@ -2338,7 +2338,7 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], o res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) @@ -2546,7 +2546,7 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: O res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) @@ -2586,7 +2586,7 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) @@ -2704,7 +2704,7 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) @@ -2744,7 +2744,7 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple res = CrudResult(crud_resp[0]) if crud_resp[1] is not None: - errs = list() + errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) raise CrudModuleManyError(res, errs) From 91949447c61031de3eb1acbba82686009ef0e74a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:33:00 +0300 Subject: [PATCH 099/190] pylint: fix consider-merging-isinstance case Fix the only case of R1701 consider-merging-isinstance. Part of #270 --- tarantool/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarantool/schema.py b/tarantool/schema.py index 97ce8a48..65e9881d 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -70,7 +70,7 @@ def to_unicode_recursive(value, max_depth): res[key] = val return res - if isinstance(value, list) or isinstance(value, tuple): + if isinstance(value, (list, tuple)): res = [] for item in value: item = to_unicode_recursive(item, max_depth - 1) From b1c8b7e683f0f7bccc76f42197c004d8c0a40c8b Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:43:27 +0300 Subject: [PATCH 100/190] pylint: fix no-else-return cases Fix all cases of R1705 no-else-return. Part of #270 --- tarantool/connection.py | 40 ++++++++++++------------- tarantool/connection_pool.py | 8 ++--- tarantool/error.py | 4 +-- tarantool/mesh_connection.py | 3 +- tarantool/msgpack_ext/datetime.py | 8 ++--- tarantool/msgpack_ext/types/datetime.py | 11 ++++--- tarantool/utils.py | 2 +- test/suites/lib/tarantool_server.py | 6 ++-- 8 files changed, 41 insertions(+), 41 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 2b875ee0..c8ba9c96 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1207,26 +1207,26 @@ def check(): # Check that connection is alive if exc.errno == errno.EBADF: return errno.ECONNRESET return exc.errno + + if os.name == 'nt': + flag = socket.MSG_PEEK + self._socket.setblocking(False) else: - if os.name == 'nt': - flag = socket.MSG_PEEK - self._socket.setblocking(False) - else: - flag = socket.MSG_DONTWAIT | socket.MSG_PEEK - retbytes = self._sys_recv(sock_fd, buf, 1, flag) - - err = 0 - if os.name!= 'nt': - err = ctypes.get_errno() - else: - err = ctypes.get_last_error() - self._socket.setblocking(True) - - if (retbytes < 0) and err in (errno.EAGAIN, errno.EWOULDBLOCK, WWSAEWOULDBLOCK): - ctypes.set_errno(0) - return errno.EAGAIN - else: - return errno.ECONNRESET + flag = socket.MSG_DONTWAIT | socket.MSG_PEEK + retbytes = self._sys_recv(sock_fd, buf, 1, flag) + + err = 0 + if os.name!= 'nt': + err = ctypes.get_errno() + else: + err = ctypes.get_last_error() + self._socket.setblocking(True) + + if (retbytes < 0) and err in (errno.EAGAIN, errno.EWOULDBLOCK, WWSAEWOULDBLOCK): + ctypes.set_errno(0) + return errno.EAGAIN + + return errno.ECONNRESET last_errno = check() if self.connected and last_errno == errno.EAGAIN: @@ -1561,7 +1561,7 @@ def _join_v17(self, server_uuid): yield resp if resp.code >= REQUEST_TYPE_ERROR: return - elif resp.code == REQUEST_TYPE_OK: + if resp.code == REQUEST_TYPE_OK: if state == JoinState.HANDSHAKE: state = JoinState.INITIAL elif state == JoinState.INITIAL: diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 6a44c13d..d8418909 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -308,15 +308,15 @@ def getnext(self, mode): if mode == Mode.ANY: return self._getnext_by_mode(self.any_iter) - elif mode == Mode.RW: + if mode == Mode.RW: return self._getnext_by_mode(self.rw_iter, err_msg="Can't find healthy rw instance in pool") - elif mode == Mode.RO: + if mode == Mode.RO: return self._getnext_by_mode(self.ro_iter, err_msg="Can't find healthy ro instance in pool") - elif mode == Mode.PREFER_RO: + if mode == Mode.PREFER_RO: return self._getnext_by_mode(self.ro_iter, self.rw_iter) - elif mode == Mode.PREFER_RW: + if mode == Mode.PREFER_RW: return self._getnext_by_mode(self.rw_iter, self.ro_iter) raise ValueError(f"Unexpected mode {mode}") diff --git a/tarantool/error.py b/tarantool/error.py index 35ca3007..f3a9ff4b 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -186,8 +186,8 @@ def os_strerror_patched(code): message = os_strerror_orig(code) if not message.startswith("Unknown"): return message - else: - return _code2str.get(code, f"Unknown error {code}") + + return _code2str.get(code, f"Unknown error {code}") os.strerror = os_strerror_patched del os_strerror_patched diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 9d70b4ab..58f95e2c 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -159,7 +159,8 @@ def format_error(address, err): # Looks okay. return result, None - elif isinstance(result['port'], str): + + if isinstance(result['port'], str): # Looks like a unix address. # Expect no host. diff --git a/tarantool/msgpack_ext/datetime.py b/tarantool/msgpack_ext/datetime.py index 8964245e..7b78ac6c 100644 --- a/tarantool/msgpack_ext/datetime.py +++ b/tarantool/msgpack_ext/datetime.py @@ -169,9 +169,9 @@ def decode(data, _): tz = tt_timezones.indexToTimezone[tzindex] return Datetime(timestamp=seconds, nsec=nsec, tz=tz, timestamp_since_utc_epoch=True) - elif tzoffset != 0: + if tzoffset != 0: return Datetime(timestamp=seconds, nsec=nsec, tzoffset=tzoffset, timestamp_since_utc_epoch=True) - else: - return Datetime(timestamp=seconds, nsec=nsec, - timestamp_since_utc_epoch=True) + + return Datetime(timestamp=seconds, nsec=nsec, + timestamp_since_utc_epoch=True) diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index e214d987..95f3e8eb 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -480,10 +480,10 @@ def __sub__(self, other): sec = self_dt.second - other_dt.second, nsec = self_nsec - other_nsec, ) - elif isinstance(other, Interval): + if isinstance(other, Interval): return self._interval_operation(other, sign=-1) - else: - raise TypeError(f"unsupported operand type(s) for -: '{type(self)}' and '{type(other)}'") + + raise TypeError(f"unsupported operand type(s) for -: '{type(self)}' and '{type(other)}'") def __eq__(self, other): """ @@ -498,10 +498,9 @@ def __eq__(self, other): if isinstance(other, Datetime): return self._datetime == other._datetime - elif isinstance(other, pandas.Timestamp): + if isinstance(other, pandas.Timestamp): return self._datetime == other - else: - return False + return False def __str__(self): return self._datetime.__str__() diff --git a/tarantool/utils.py b/tarantool/utils.py index 775a827a..296717f8 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -45,7 +45,7 @@ def wrap_key(*args, first=True, select=False): if len(args) == 1: if isinstance(args[0], (list, tuple)) and first: return wrap_key(*args[0], first=False, select=select) - elif args[0] is None and select: + if args[0] is None and select: return [] return list(args) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 2480cebb..7fc8cce2 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -310,10 +310,10 @@ def wait_until_started(self): if ans in ('running', 'hot_standby', 'orphan') or ans.startswith('replica'): temp.disconnect() return True - elif ans in ('loading',): + if ans in ('loading',): continue - else: - raise ValueError(f"Strange output for `box.info.status`: {ans}") + + raise ValueError(f"Strange output for `box.info.status`: {ans}") except socket.error as exc: if exc.errno == errno.ECONNREFUSED: time.sleep(0.1) From 52d6fe9fe20e30b51edd73452ecb89c86db5f773 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:45:28 +0300 Subject: [PATCH 101/190] pylint: fix no-else-raise cases Fix all cases of R1720 no-else-raise. Part of #270 --- tarantool/connection.py | 18 +++++++++--------- tarantool/schema.py | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index c8ba9c96..f2d879ce 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1114,15 +1114,15 @@ def _recv(self, to_read): "Lost connection to server during query" ) raise NetworkError(err) from exc - else: - if len(tmp) == 0: - err = socket.error( - errno.ECONNRESET, - "Lost connection to server during query" - ) - raise NetworkError(err) - to_read -= len(tmp) - buf += tmp + + if len(tmp) == 0: + err = socket.error( + errno.ECONNRESET, + "Lost connection to server during query" + ) + raise NetworkError(err) + to_read -= len(tmp) + buf += tmp return buf def _read_response(self): diff --git a/tarantool/schema.py b/tarantool/schema.py index 65e9881d..4cee7cdd 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -240,7 +240,7 @@ def fetch_space(self, space): raise SchemaError( 'Some strange output from server: \n' + str(space_row) ) - elif len(space_row) == 0 or not space_row[0]: + if len(space_row) == 0 or not space_row[0]: # We can't find space with this name or id temp_name = 'name' if isinstance(space, str) else 'id' errmsg = f"There's no space with {temp_name} '{space}'" @@ -352,7 +352,7 @@ def fetch_index(self, space_object, index): raise SchemaError( 'Some strange output from server: \n' + str(index_row) ) - elif len(index_row) == 0 or not index_row[0]: + if len(index_row) == 0 or not index_row[0]: # We can't find index with this name or id temp_name = 'name' if isinstance(index, str) else 'id' errmsg = (f"There's no index with {temp_name} '{index}'" From 2ab71b1a58552693fbcabcfb19316da3b4d5936b Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 16:47:31 +0300 Subject: [PATCH 102/190] pylint: disable consider-using-with cases Disable all cases of R1732 consider-using-with in test server code: the implementation doesn't allows to use with. Part of #270 --- test/suites/lib/tarantool_server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 7fc8cce2..0daf8168 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -160,6 +160,7 @@ def log_des(self): """ Get server log file descriptor. """ + # pylint: disable=consider-using-with if self._log_des is None: self._log_des = open(self.logfile_path, 'a') @@ -204,6 +205,8 @@ def __init__(self, ssl_password_file=None, create_unix_socket=False, auth_type=None): + # pylint: disable=consider-using-with + os.popen('ulimit -c unlimited').close() if create_unix_socket: @@ -332,6 +335,7 @@ def start(self): * Wait unitl Tarantool\\Box started --DONE(wait_until_started) """ + # pylint: disable=consider-using-with self.generate_configuration() if self.script: From 2477322b81028ca344bad645902b066380957536 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 17:29:07 +0300 Subject: [PATCH 103/190] pylint: fix keyword-arg-before-vararg cases Fix all cases of W1113 keyword-arg-before-vararg. Part of #270 --- tarantool/error.py | 61 +++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tarantool/error.py b/tarantool/error.py index f3a9ff4b..6ad771de 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -6,11 +6,6 @@ import os import socket -try: - import ssl - IS_SSL_SUPPORTED = True -except ImportError: - IS_SSL_SUPPORTED = False import sys import warnings @@ -236,27 +231,35 @@ class NetworkError(DatabaseError): Error related to network. """ - def __init__(self, orig_exception=None, *args, **kwargs): + def __init__(self, *args, **kwargs): """ - :param orig_exception: Exception to wrap. - :type orig_exception: optional + :param args: Exception arguments. If the first argument is + a socket exception, it is wrapped. + :type args: :obj:`tuple`, optional - :param args: Wrapped exception arguments. - :type args: :obj:`tuple` + :param kwargs: Exception to wrap. + :type args: :obj:`dict`, optional """ self.errno = 0 - if hasattr(orig_exception, 'errno'): - self.errno = orig_exception.errno - if orig_exception: + + if len(args) > 0: + orig_exception = args[0] + + if hasattr(orig_exception, 'errno'): + self.errno = orig_exception.errno + if isinstance(orig_exception, socket.timeout): self.message = "Socket timeout" super().__init__(0, self.message) - elif isinstance(orig_exception, socket.error): + return + + if isinstance(orig_exception, socket.error): self.message = os.strerror(orig_exception.errno) super().__init__(orig_exception.errno, self.message) - else: - super().__init__(orig_exception, *args, **kwargs) + return + + super().__init__(*args, **kwargs) class NetworkWarning(UserWarning): @@ -269,23 +272,25 @@ class SslError(DatabaseError): Error related to SSL. """ - def __init__(self, orig_exception=None, *args): + def __init__(self, *args, **kwargs): """ - :param orig_exception: Exception to wrap. - :type orig_exception: optional + :param args: Exception arguments. If the first argument is + a socket exception, it is wrapped. + :type args: :obj:`tuple`, optional - :param args: Wrapped exception arguments. - :type args: :obj:`tuple` + :param kwargs: Exception to wrap. + :type args: :obj:`dict`, optional """ self.errno = 0 - if hasattr(orig_exception, 'errno'): - self.errno = orig_exception.errno - if orig_exception: - if IS_SSL_SUPPORTED and isinstance(orig_exception, ssl.SSLError): - super().__init__(orig_exception, *args) - else: - super().__init__(orig_exception, *args) + + if len(args) > 0: + orig_exception = args[0] + + if hasattr(orig_exception, 'errno'): + self.errno = orig_exception.errno + + super().__init__(*args, **kwargs) class ClusterDiscoveryWarning(UserWarning): From 43e8457e44449becc7010d290527315d75a02fb5 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 17:33:45 +0300 Subject: [PATCH 104/190] pylint: fix redundant-u-string-prefix cases Fix all cases of W1406 redundant-u-string-prefix. Part of #270 --- docs/source/conf.py | 16 ++++++++-------- test/suites/test_schema.py | 12 +++--------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 4e08ccea..d6050c94 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,8 +48,8 @@ master_doc = 'index' # General information about the project. -project = u'Tarantool python client library' -copyright = u'2011-2022, tarantool-python AUTHORS' +project = 'Tarantool python client library' +copyright = '2011-2022, tarantool-python AUTHORS' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -280,8 +280,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Tarantoolpythonclientlibrary.tex', u'Tarantool python client library Documentation', - u'tarantool-python AUTHORS', 'manual'), + ('index', 'Tarantoolpythonclientlibrary.tex', 'Tarantool python client library Documentation', + 'tarantool-python AUTHORS', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -310,8 +310,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'tarantoolpythonclientlibrary', u'Tarantool python client library Documentation', - [u'tarantool-python AUTHORS'], 1) + ('index', 'tarantoolpythonclientlibrary', 'Tarantool python client library Documentation', + ['tarantool-python AUTHORS'], 1) ] # If true, show URL addresses after external links. @@ -324,8 +324,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Tarantoolpythonclientlibrary', u'Tarantool python client library Documentation', - u'tarantool-python AUTHORS', 'Tarantoolpythonclientlibrary', 'One line description of project.', + ('index', 'Tarantoolpythonclientlibrary', 'Tarantool python client library Documentation', + 'tarantool-python AUTHORS', 'Tarantoolpythonclientlibrary', 'One line description of project.', 'Miscellaneous'), ] diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 7e89b130..af0e209a 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -94,15 +94,9 @@ def setUpClass(cls): fetch_schema=False) cls.sch = cls.con.schema - # The relevant test cases mainly target Python 2, where - # a user may want to pass a string literal as a space or - # an index name and don't bother whether all symbols in it - # are ASCII. cls.unicode_space_name_literal = '∞' cls.unicode_index_name_literal = '→' - cls.unicode_space_name_u = u'∞' - cls.unicode_index_name_u = u'→' cls.unicode_space_id, cls.unicode_index_id = cls.srv.admin(""" do local space = box.schema.create_space('\\xe2\\x88\\x9e') @@ -164,13 +158,13 @@ def fetch_count(self): def verify_unicode_space(self, space): self.assertEqual(space.sid, self.unicode_space_id) - self.assertEqual(space.name, self.unicode_space_name_u) + self.assertEqual(space.name, self.unicode_space_name_literal) self.assertEqual(space.arity, 1) def verify_unicode_index(self, index): - self.assertEqual(index.space.name, self.unicode_space_name_u) + self.assertEqual(index.space.name, self.unicode_space_name_literal) self.assertEqual(index.iid, self.unicode_index_id) - self.assertEqual(index.name, self.unicode_index_name_u) + self.assertEqual(index.name, self.unicode_index_name_literal) self.assertEqual(len(index.parts), 1) def test_01_space_bad(self): From 2ee10b3c2990b67d7ed26ddaa832e618ca335057 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 17:42:33 +0300 Subject: [PATCH 105/190] pylint: fix unspecified-encoding cases Fix all cases of W1514 unspecified-encoding. Part of #270 --- setup.py | 4 ++-- tarantool/connection.py | 2 +- test/suites/lib/tarantool_server.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 46bf4cbb..d9dd40a0 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ def run(self): package_dir = self.get_package_dir('tarantool') version_file = os.path.join(package_dir, 'version.py') - with open(version_file, 'w') as file: + with open(version_file, 'w', encoding='utf-8') as file: file.write(f"__version__ = '{version}'") return super().run() @@ -80,7 +80,7 @@ def get_dependencies(filename): root = os.path.dirname(os.path.realpath(__file__)) requirements = os.path.join(root, filename) if os.path.isfile(requirements): - with open(requirements) as file: + with open(requirements, encoding='utf-8') as file: return file.read().splitlines() raise RuntimeError("Unable to get dependencies from file " + filename) diff --git a/tarantool/connection.py b/tarantool/connection.py index f2d879ce..f76358bf 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1008,7 +1008,7 @@ def _ssl_load_cert_chain(self, context): if self.ssl_password_file is not None: - with open(self.ssl_password_file) as file: + with open(self.ssl_password_file, encoding=self.encoding) as file: for line in file: try: context.load_cert_chain(certfile=self.ssl_cert_file, diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 0daf8168..012f5d35 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -163,7 +163,7 @@ def log_des(self): # pylint: disable=consider-using-with if self._log_des is None: - self._log_des = open(self.logfile_path, 'a') + self._log_des = open(self.logfile_path, 'a', encoding='utf-8') return self._log_des @log_des.deleter From 3090a48d52a76e6cc217cb9b43f200a36d77e6ee Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 17:48:02 +0300 Subject: [PATCH 106/190] setup: migrate from distutils distutils is starting to get removed from the standard library since 2020 and has been phased out since 2014 (or earlier). Distutils has been exposed only recently, so relatively old Python (like 3.8) setuptools doesn't have them yet. 1. https://peps.python.org/pep-0632 2. https://github.com/pypa/setuptools/commit/974dbb03769a500c5077d37291817f30359a0d7b Part of #270 --- setup.py | 8 ++------ test/setup_command.py | 9 +++++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index d9dd40a0..4473f52e 100755 --- a/setup.py +++ b/setup.py @@ -6,12 +6,8 @@ import codecs import os -try: - from setuptools import setup, find_packages - from setuptools.command.build_py import build_py -except ImportError: - from distutils.core import setup, find_packages - from distutils.command.build_py import build_py +from setuptools import setup, find_packages +from setuptools.command.build_py import build_py # Extra commands for documentation management cmdclass = {} diff --git a/test/setup_command.py b/test/setup_command.py index a39bb599..517e0e06 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -4,10 +4,15 @@ """ import unittest -from distutils.errors import DistutilsError import setuptools +try: + from setuptools.errors import BaseError +except ModuleNotFoundError: + # pylint: disable=deprecated-module + from distutils.errors import DistutilsError as BaseError + class Test(setuptools.Command): """ Class implementing `python setup.py test`. @@ -37,4 +42,4 @@ def run(self): test_runner = unittest.TextTestRunner(verbosity=2) result = test_runner.run(tests) if not result.wasSuccessful(): - raise DistutilsError('There are failed tests') + raise BaseError('There are failed tests') From cf500e02883f563b6d1310f2c61cf0fdcfcd3d1d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 17:56:26 +0300 Subject: [PATCH 107/190] pylint: fix unnecessary-lambda-assignment cases Fix all cases of C3001 unnecessary-lambda-assignment. Part of #270 --- tarantool/request.py | 5 +++-- tarantool/response.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tarantool/request.py b/tarantool/request.py index 99b9d5f0..8773602a 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -115,8 +115,9 @@ def packer_factory(conn): # We need configured packer to work with error extention # type payload, but module do not provide access to self # inside extension type packers. - packer_no_ext = msgpack.Packer(**packer_kwargs) - default = lambda obj: packer_default(obj, packer_no_ext) + def default(obj): + packer_no_ext = msgpack.Packer(**packer_kwargs) + return packer_default(obj, packer_no_ext) packer_kwargs['default'] = default return msgpack.Packer(**packer_kwargs) diff --git a/tarantool/response.py b/tarantool/response.py index f98fc6e7..ac253df2 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -78,8 +78,9 @@ def unpacker_factory(conn): # We need configured unpacker to work with error extention # type payload, but module do not provide access to self # inside extension type unpackers. - unpacker_no_ext = msgpack.Unpacker(**unpacker_kwargs) - ext_hook = lambda code, data: unpacker_ext_hook(code, data, unpacker_no_ext) + def ext_hook(code, data): + unpacker_no_ext = msgpack.Unpacker(**unpacker_kwargs) + return unpacker_ext_hook(code, data, unpacker_no_ext) unpacker_kwargs['ext_hook'] = ext_hook return msgpack.Unpacker(**unpacker_kwargs) From 95fe35ebe9deeb5e879bfb25649ca1ea9d276cae Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:01:19 +0300 Subject: [PATCH 108/190] pylint: fix no-member case Fix the only case of E1101 no-member. Part of #270 --- test/suites/test_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index af0e209a..b3e59219 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -131,6 +131,7 @@ def setUpClass(cls): box.space.constr_tester_2:create_index('I1', { parts = {'id'} }) box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} }) """) + cls.encoding = None def setUp(self): # prevent a remote tarantool from clean our session From b4c28ee71ecb059d2c37710442e94f7eeb25c07d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:02:37 +0300 Subject: [PATCH 109/190] pylint: fix unnecessary-dunder-call case Fix the only case of C2801 unnecessary-dunder-call. Part of #270 --- test/suites/test_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index b3e59219..d342a574 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -456,7 +456,7 @@ def test_07_schema_version_update(self): 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: + if int(str(self.srv.admin.tnt_version)[0]) > 1: self.testing_methods['available']['execute'] = { 'input': ['SELECT * FROM "tester"'], 'output': [[1, None]], From f5e344635a5063b0ced35050b1140b727258bb84 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:04:06 +0300 Subject: [PATCH 110/190] pylint: disable too-many-function-args cases in tests Disable cases of E1121 too-many-function-args in tests. Part of #270 --- test/suites/test_datetime.py | 2 +- test/suites/test_interval.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 80a43b73..931fb512 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,7 +1,7 @@ """ This module tests work with datetime type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,too-many-function-args import sys import re diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 26c39c88..486d19ba 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,7 +1,7 @@ """ This module tests work with datetime interval type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,too-many-function-args import re import sys From 9ead2ed85df6ad37689c15d93068590016655215 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:19:57 +0300 Subject: [PATCH 111/190] pylint: allow long links and tables in docstrings Part of #270 --- .pylintrc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pylintrc b/.pylintrc index 1f506706..3fd8b942 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,3 +2,7 @@ # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_,ok,t,tz + +[FORMAT] +# Allow links in docstings, allow tables +ignore-long-lines=^(?:\s*(# )?(?:\.\.\s.+?:)?\s*?)|(\s\+.+\+)|(\s\|.+\|)$ From d1d81e7811c3b3dc136445c745f53756b421a6b2 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:50:52 +0300 Subject: [PATCH 112/190] pylint: fix line-too-long cases Fix several cases of C0301 line-too-long. Part of #270 --- tarantool/connection.py | 55 +++++++++++++++++-------- tarantool/connection_pool.py | 21 ++++++---- tarantool/msgpack_ext/types/datetime.py | 3 +- tarantool/msgpack_ext/types/interval.py | 6 ++- test/suites/test_crud.py | 51 ++++++++++++++++++----- test/suites/test_datetime.py | 3 +- test/suites/test_decimal.py | 6 ++- test/suites/test_dml.py | 54 ++++++++++++++++-------- test/suites/test_error_ext.py | 6 ++- test/suites/test_interval.py | 3 +- test/suites/test_pool.py | 3 +- test/suites/test_schema.py | 20 +++++---- 12 files changed, 159 insertions(+), 72 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index f76358bf..cbe075a4 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1503,11 +1503,13 @@ def _get_auth_type(self): auth_type = AUTH_TYPE_CHAP_SHA1 else: if self._server_auth_type not in AUTH_TYPES: - raise ConfigurationError(f'Unknown server authentication type {self._server_auth_type}') + raise ConfigurationError('Unknown server authentication type ' + + str(self._server_auth_type)) auth_type = self._server_auth_type else: if self._client_auth_type not in AUTH_TYPES: - raise ConfigurationError(f'Unknown client authentication type {self._client_auth_type}') + raise ConfigurationError('Unknown client authentication type ' + + str(self._client_auth_type)) auth_type = self._client_auth_type if auth_type == AUTH_TYPE_PAP_SHA256 and self.transport != SSL_TRANSPORT: @@ -1900,7 +1902,8 @@ def ping(self, notime=False): return "Success" return finish_time - start_time - def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, iterator=None, on_push=None, on_push_ctx=None): + def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, iterator=None, + on_push=None, on_push_ctx=None): """ Execute a SELECT request: `select`_ a tuple from the space. @@ -2199,7 +2202,8 @@ def _packer_factory(self): def _unpacker_factory(self): return self._unpacker_factory_impl(self) - def crud_insert(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_insert(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Inserts row through the `crud `__. @@ -2232,7 +2236,8 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], opts: Optiona return CrudResult(crud_resp[0]) - def crud_insert_object(self, space_name: str, values: dict, opts: Optional[dict]=None) -> CrudResult: + def crud_insert_object(self, space_name: str, values: dict, + opts: Optional[dict]=None) -> CrudResult: """ Inserts object row through the `crud `__. @@ -2265,7 +2270,8 @@ def crud_insert_object(self, space_name: str, values: dict, opts: Optional[dict] return CrudResult(crud_resp[0]) - def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_insert_many(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Inserts batch rows through the `crud `__. @@ -2305,7 +2311,8 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], opts: Op return res - def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Inserts batch object rows through the `crud `__. @@ -2377,7 +2384,8 @@ def crud_get(self, space_name: str, key: int, opts: Optional[dict]=None) -> Crud return CrudResult(crud_resp[0]) - def crud_update(self, space_name: str, key: int, operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: + def crud_update(self, space_name: str, key: int, operations: Optional[list]=None, + opts: Optional[dict]=None) -> CrudResult: """ Updates row through the `crud `__. @@ -2447,7 +2455,8 @@ def crud_delete(self, space_name: str, key: int, opts: Optional[dict]=None) -> C return CrudResult(crud_resp[0]) - def crud_replace(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_replace(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Replaces row through the `crud `__. @@ -2480,7 +2489,8 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], opts: Option return CrudResult(crud_resp[0]) - def crud_replace_object(self, space_name: str, values: dict, opts: Optional[dict]=None) -> CrudResult: + def crud_replace_object(self, space_name: str, values: dict, + opts: Optional[dict]=None) -> CrudResult: """ Replaces object row through the `crud `__. @@ -2513,7 +2523,8 @@ def crud_replace_object(self, space_name: str, values: dict, opts: Optional[dict return CrudResult(crud_resp[0]) - def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_replace_many(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Replaces batch rows through the `crud `__. @@ -2553,7 +2564,8 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], opts: O return res - def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Replaces batch object rows through the `crud `__. @@ -2593,7 +2605,8 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], return res - def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: + def crud_upsert(self, space_name: str, values: Union[tuple, list], + operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: """ Upserts row through the `crud `__. @@ -2632,7 +2645,9 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], operations: O return CrudResult(crud_resp[0]) - def crud_upsert_object(self, space_name: str, values: dict, operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: + def crud_upsert_object(self, space_name: str, values: dict, + operations: Optional[list]=None, + opts: Optional[dict]=None) -> CrudResult: """ Upserts object row through the `crud `__. @@ -2671,7 +2686,8 @@ def crud_upsert_object(self, space_name: str, values: dict, operations: Optional return CrudResult(crud_resp[0]) - def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Upserts batch rows through the `crud `__. @@ -2711,7 +2727,8 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] return res - def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], opts: Optional[dict]=None) -> CrudResult: + def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], + opts: Optional[dict]=None) -> CrudResult: """ Upserts batch object rows through the `crud `__. @@ -2751,7 +2768,8 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple return res - def crud_select(self, space_name: str, conditions: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: + def crud_select(self, space_name: str, conditions: Optional[list]=None, + opts: Optional[dict]=None) -> CrudResult: """ Selects rows through the `crud `__. @@ -2939,7 +2957,8 @@ def crud_storage_info(self, opts: Optional[dict]=None) -> dict: return crud_resp[0] - def crud_count(self, space_name: str, conditions: Optional[list]=None, opts: Optional[dict]=None) -> int: + def crud_count(self, space_name: str, conditions: Optional[list]=None, + opts: Optional[dict]=None) -> int: """ Gets rows count through the `crud `__. diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index d8418909..bd974038 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -553,14 +553,16 @@ def _get_new_state(self, unit): try: conn.connect() except NetworkError as exc: - msg = f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}, reason: {repr(exc)}" + msg = (f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}, " + f"reason: {repr(exc)}") warn(msg, ClusterConnectWarning) return InstanceState(Status.UNHEALTHY) try: resp = conn.call('box.info') except NetworkError as exc: - msg = f"Failed to get box.info for {unit.addr['host']}:{unit.addr['port']}, reason: {repr(exc)}" + msg = (f"Failed to get box.info for {unit.addr['host']}:{unit.addr['port']}, " + f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) @@ -813,7 +815,8 @@ def replace(self, space_name, values, *, mode=Mode.RW, on_push=None, on_push_ctx .. _replace: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/replace/ """ - return self._send(mode, 'replace', space_name, values, on_push=on_push, on_push_ctx=on_push_ctx) + return self._send(mode, 'replace', space_name, values, + on_push=on_push, on_push_ctx=on_push_ctx) def insert(self, space_name, values, *, mode=Mode.RW, on_push=None, on_push_ctx=None): """ @@ -842,7 +845,8 @@ def insert(self, space_name, values, *, mode=Mode.RW, on_push=None, on_push_ctx= .. _insert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/insert/ """ - return self._send(mode, 'insert', space_name, values, on_push=on_push, on_push_ctx=on_push_ctx) + return self._send(mode, 'insert', space_name, values, + on_push=on_push, on_push_ctx=on_push_ctx) def delete(self, space_name, key, *, index=0, mode=Mode.RW, on_push=None, on_push_ctx=None): """ @@ -874,9 +878,11 @@ def delete(self, space_name, key, *, index=0, mode=Mode.RW, on_push=None, on_pus .. _delete: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/delete/ """ - return self._send(mode, 'delete', space_name, key, index=index, on_push=on_push, on_push_ctx=on_push_ctx) + return self._send(mode, 'delete', space_name, key, index=index, + on_push=on_push, on_push_ctx=on_push_ctx) - def upsert(self, space_name, tuple_value, op_list, *, index=0, mode=Mode.RW, on_push=None, on_push_ctx=None): + def upsert(self, space_name, tuple_value, op_list, *, index=0, mode=Mode.RW, + on_push=None, on_push_ctx=None): """ Execute an UPSERT request on the pool server: `upsert`_ a tuple to the space. Refer to :meth:`~tarantool.Connection.upsert`. @@ -912,7 +918,8 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, mode=Mode.RW, on_ return self._send(mode, 'upsert', space_name, tuple_value, op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) - def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, on_push=None, on_push_ctx=None): + def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, + on_push=None, on_push_ctx=None): """ Execute an UPDATE request on the pool server: `update`_ a tuple in the space. Refer to :meth:`~tarantool.Connection.update`. diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 95f3e8eb..7057540b 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -423,7 +423,8 @@ def __add__(self, other): """ if not isinstance(other, Interval): - raise TypeError(f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'") + raise TypeError("unsupported operand type(s) for +: " + f"'{type(self)}' and '{type(other)}'") return self._interval_operation(other, sign=1) diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index f9098a4b..53aadf9a 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -120,7 +120,8 @@ def __add__(self, other): """ if not isinstance(other, Interval): - raise TypeError(f"unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'") + raise TypeError("unsupported operand type(s) for +: " + f"'{type(self)}' and '{type(other)}'") # Tarantool saves adjust of the first argument # @@ -168,7 +169,8 @@ def __sub__(self, other): """ if not isinstance(other, Interval): - raise TypeError(f"unsupported operand type(s) for -: '{type(self)}' and '{type(other)}'") + raise TypeError("unsupported operand type(s) for -: " + f"'{type(self)}' and '{type(other)}'") # Tarantool saves adjust of the first argument # diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index c7eed96b..67b13190 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -86,7 +86,8 @@ def setUp(self): }, 'error': { 'input': { - 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Logan'}, {'timeout': 10}], + 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Logan'}, + {'timeout': 10}], }, 'output': { 'str': [ @@ -266,7 +267,10 @@ def setUp(self): 'crud_replace_object': { 'success': { 'input': { - 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Eliza'}, {'timeout': 10}], + 'args': [ + 'tester', {'id': 2, 'bucket_id': 100, 'name': 'Eliza'}, + {'timeout': 10} + ], }, 'output': { 'rows': [[2, 100, 'Eliza']], @@ -374,7 +378,8 @@ def setUp(self): 'crud_upsert': { 'success': { 'input': { - 'args': ['tester', [2, 100, 'Cephus'], [['+', 'bucket_id', 1]], {'timeout': 10}], + 'args': ['tester', [2, 100, 'Cephus'], [['+', 'bucket_id', 1]], + {'timeout': 10}], }, 'output': { 'rows': [], @@ -455,10 +460,22 @@ def setUp(self): 'args': [ 'tester', [ - [{'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, [['+', 'bucket_id', 1]]], - [{'id': 3, 'bucket_id': 100, 'name': 'Esau'}, [['+', 'bucket_id', 1]]], - [{'id': 4, 'bucket_id': 100, 'name': 'Haman'}, [['+', 'bucket_id', 1]]], - [{'id': 5, 'bucket_id': 100, 'name': 'Gershon'}, [['+', 'bucket_id', 1]]], + [ + {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 3, 'bucket_id': 100, 'name': 'Esau'}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 4, 'bucket_id': 100, 'name': 'Haman'}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 5, 'bucket_id': 100, 'name': 'Gershon'}, + [['+', 'bucket_id', 1]] + ], ], {'timeout': 10}, ], @@ -470,10 +487,22 @@ def setUp(self): 'args': [ 'tester', [ - [{'id': 3, 'bucket_id': 100, 'name': 'Ephron'}, [['+', 'bucket_id', 1]]], - [{'id': 4, 'bucket_id': 100, 'name': 'Ethan'}, [['+', 'bucket_id', 1]]], - [{'id': 7, 'bucket_id': 100, 'name': 0}, [['+', 'bucket_id', 1]]], - [{'id': 8, 'bucket_id': 100, 'name': 0}, [['+', 'bucket_id', 1]]], + [ + {'id': 3, 'bucket_id': 100, 'name': 'Ephron'}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 4, 'bucket_id': 100, 'name': 'Ethan'}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 7, 'bucket_id': 100, 'name': 0}, + [['+', 'bucket_id', 1]] + ], + [ + {'id': 8, 'bucket_id': 100, 'name': 0}, + [['+', 'bucket_id', 1]] + ], ], {'timeout': 10}, ], diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 931fb512..e429553b 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -279,7 +279,8 @@ def test_datetime_class_invalid_init(self): 'python': tarantool.Datetime(timestamp=1661958474, nsec=308543321, tz='Europe/Moscow', timestamp_since_utc_epoch=True), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\xb3\x03'), - 'tarantool': r"datetime.new({timestamp=1661969274, nsec=308543321, tz='Europe/Moscow'})", + 'tarantool': r"datetime.new({timestamp=1661969274, nsec=308543321, " + + r"tz='Europe/Moscow'})", }, } diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 4598ea2a..01c85ad3 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -367,13 +367,15 @@ def test_tarantool_encode_error(self): 'tarantool': "decimal.new('-99999999999999999999999999999999999999')", }, 'decimal_limit_break_tail_9': { - 'python': decimal.Decimal('99999999999999999999999999999999999999.1111111111111111111111111'), + 'python': decimal.Decimal('99999999999999999999999999999999999999.11111111111111' + '11111111111'), 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9c'), 'tarantool': "decimal.new('99999999999999999999999999999999999999')", }, 'decimal_limit_break_tail_10': { - 'python': decimal.Decimal('-99999999999999999999999999999999999999.1111111111111111111111111'), + 'python': decimal.Decimal('-99999999999999999999999999999999999999.11111111111111' + '11111111111'), 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9d'), 'tarantool': "decimal.new('-99999999999999999999999999999999999999')", diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 5025c140..58048895 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -88,10 +88,13 @@ def test_02_select(self): # Check that select with different keys are Ok. (With and without index names) self.assertSequenceEqual(self.con.select('space_1', 20), [[20, 0, 'tuple_20']]) self.assertSequenceEqual(self.con.select('space_1', [21]), [[21, 1, 'tuple_21']]) - self.assertSequenceEqual(self.con.select('space_1', [22], index='primary'), [[22, 2, 'tuple_22']]) - self.assertSequenceEqual(self.con.select('space_1', [23], index='primary'), [[23, 3, 'tuple_23']]) + self.assertSequenceEqual(self.con.select('space_1', [22], index='primary'), + [[22, 2, 'tuple_22']]) + self.assertSequenceEqual(self.con.select('space_1', [23], index='primary'), + [[23, 3, 'tuple_23']]) # Check that Offset and Limit args are working fine. - self.assertSequenceEqual(self.con.select('space_1', [20], index='primary', limit=1), [[20, 0, 'tuple_20']]) + self.assertSequenceEqual(self.con.select('space_1', [20], index='primary', limit=1), + [[20, 0, 'tuple_20']]) # With other indexes too self.assertSequenceEqual( sorted( @@ -121,14 +124,17 @@ def test_02_select(self): self.assertTrue(i[2] == 'tuple_' + str(i[0])) # Check limit again. - self.assertEqual(len(self.con.select('space_1', [0, 'tuple_20'], index='secondary', limit=0)), 0) + self.assertEqual( + len(self.con.select('space_1', [0, 'tuple_20'], index='secondary', limit=0)), + 0) self.assertEqual(len(self.con.select('space_1', [0], index='secondary', limit=0)), 0) self.assertEqual(len(self.con.select('space_1', [0], index='secondary', limit=100)), 99) self.assertEqual(len(self.con.select('space_1', [0], index='secondary', limit=50)), 50) # TODO: Check iterator_types self.assertSequenceEqual( - self.con.select('space_1', [0, 'tuple_20'], index='secondary', limit=2, iterator=tarantool.const.ITERATOR_GT), + self.con.select('space_1', [0, 'tuple_20'], index='secondary', limit=2, + iterator=tarantool.const.ITERATOR_GT), [[200, 0, 'tuple_200'], [205, 0, 'tuple_205']] ) @@ -141,11 +147,13 @@ def test_03_delete(self): with self.assertRaisesRegex(tarantool.DatabaseError, '(19, .*)'): self.con.delete('space_1', [1, 'tuple_21']) - self.assertSequenceEqual(self.con.select('space_1', [21], index='primary'), [[21, 1, 'tuple_21']]) + self.assertSequenceEqual(self.con.select('space_1', [21], index='primary'), + [[21, 1, 'tuple_21']]) def test_04_replace(self): # Check replace that is Ok. - self.assertSequenceEqual(self.con.replace('space_1', [2, 2, 'tuple_3']), [[2, 2, 'tuple_3']]) + self.assertSequenceEqual(self.con.replace('space_1', [2, 2, 'tuple_3']), + [[2, 2, 'tuple_3']]) self.assertSequenceEqual(self.con.select('space_1', 2), [[2, 2, 'tuple_3']]) # Check replace that isn't Ok. with self.assertRaisesRegex(tarantool.DatabaseError, @@ -180,9 +188,12 @@ def test_07_call_16(self): con = tarantool.Connection(self.srv.host, self.srv.args['primary'], call_16 = True) try: con.authenticate('test', 'test') - self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), [[123, 234, 345]]) - self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), [[123, 234, 345]]) - self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), [[123, 234, 345]]) + self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), + [[123, 234, 345]]) + self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), + [[123, 234, 345]]) + self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), + [[123, 234, 345]]) with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode') with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): @@ -200,7 +211,8 @@ def test_07_call_16(self): self.assertEqual(len(ans[0]), 1) self.assertIsInstance(ans[0][0], str) - self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), [[1, 2, 3, 'fld_1']]) + self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), + [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) finally: con.close() @@ -225,7 +237,8 @@ def test_07_call_17(self): self.assertEqual(len(ans), 1) self.assertIsInstance(ans[0], str) - self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), [[1, 2, 3, 'fld_1']]) + self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), + [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) con.close() @@ -245,16 +258,21 @@ def test_08_eval(self): self.assertSequenceEqual(self.con.eval('json.decode("[123, 234, 345]")'), []) def test_09_upsert(self): - self.assertSequenceEqual(self.con.select('space_1', [22], index='primary'), [[22, 2, 'tuple_22']]) - self.assertSequenceEqual(self.con.select('space_1', [23], index='primary'), [[23, 3, 'tuple_23']]) - self.assertSequenceEqual(self.con.select('space_1', [499], index='primary'), [[499, 4, 'tuple_499']]) + self.assertSequenceEqual(self.con.select('space_1', [22], index='primary'), + [[22, 2, 'tuple_22']]) + self.assertSequenceEqual(self.con.select('space_1', [23], index='primary'), + [[23, 3, 'tuple_23']]) + self.assertSequenceEqual(self.con.select('space_1', [499], index='primary'), + [[499, 4, 'tuple_499']]) self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), []) self.assertSequenceEqual(self.con.upsert('space_1', [500, 123, 'hello, world'], [(':', 2, 2, 3, "---")]), []) - self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), [[500, 123, 'hello, world']]) + self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), + [[500, 123, 'hello, world']]) self.assertSequenceEqual(self.con.upsert('space_1', [500, 123, 'hello, world'], - [(':', 2, 2, 3, "---")]), []) - self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), [[500, 123, 'he---, world']]) + [(':', 2, 2, 3, "---")]), []) + self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), + [[500, 123, 'he---, world']]) def test_10_space(self): space = self.con.space('space_1') diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 2c2bab12..a4e29318 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -131,7 +131,8 @@ def setUp(self): type='AccessDeniedError', file='/__w/sdk/sdk/tarantool-2.10/tarantool/src/box/func.c', line=535, - message="Execute access to function 'forbidden_function' is denied for user 'no_grants'", + message=("Execute access to function 'forbidden_function' is denied " + "for user 'no_grants'"), errno=0, errcode=42, fields={ @@ -171,7 +172,8 @@ def setUp(self): type=b'AccessDeniedError', file=b'/__w/sdk/sdk/tarantool-2.10/tarantool/src/box/func.c', line=535, - message=b"Execute access to function 'forbidden_function' is denied for user 'no_grants'", + message=(b"Execute access to function 'forbidden_function' is denied " + b"for user 'no_grants'"), errno=0, errcode=42, fields={ diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 486d19ba..dc6ffc36 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -99,7 +99,8 @@ def test_interval_positional_init(self): 'datetime': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000), 'msgpack': (b'\x07\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x08\x01'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, min=2, sec=3000})", + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + r"min=2, sec=3000})", }, 'nanoseconds': { 'python': tarantool.Interval(nsec=10000000), diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 2b3792ef..6bd53c23 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -114,7 +114,8 @@ def test_00_basic(self): def test_01_roundrobin(self): self.set_cluster_ro([False, False, True, False, True]) - rw_ports = set([str(self.addrs[0]['port']), str(self.addrs[1]['port']), str(self.addrs[3]['port'])]) + rw_ports = set([str(self.addrs[0]['port']), str(self.addrs[1]['port']), + str(self.addrs[3]['port'])]) ro_ports = set([str(self.addrs[2]['port']), str(self.addrs[4]['port'])]) all_ports = set() for addr in self.addrs: diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index d342a574..c1684302 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -84,14 +84,18 @@ def setUpClass(cls): if not sys.platform.startswith("win"): # Schema fetch disable tests via mesh and pool connection # are not supported on windows platform. - cls.mesh_con_schema_disable = tarantool.MeshConnection(host=cls.srv.host, - port=cls.srv.args['primary'], - fetch_schema=False, - user='test', password='test') - cls.pool_con_schema_disable = tarantool.ConnectionPool([{'host':cls.srv.host, - 'port':cls.srv.args['primary']}], - user='test', password='test', - fetch_schema=False) + cls.mesh_con_schema_disable = tarantool.MeshConnection( + host=cls.srv.host, + port=cls.srv.args['primary'], + fetch_schema=False, + user='test', password='test') + cls.pool_con_schema_disable = tarantool.ConnectionPool( + [{ + 'host':cls.srv.host, + 'port':cls.srv.args['primary'] + }], + user='test', password='test', + fetch_schema=False) cls.sch = cls.con.schema cls.unicode_space_name_literal = '∞' From ba58604bf80fc2e69dc2cbef9c6215ffc0f6eb7d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:52:59 +0300 Subject: [PATCH 113/190] pylint: disable line-too-long cases Disable remaining cases of C0301 line-too-long. Reason: they are related to code insertion in docstrings and documentation macros. Part of #270 --- tarantool/msgpack_ext/types/datetime.py | 1 + test/suites/test_error_ext.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 7057540b..2e8d8804 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -1,6 +1,7 @@ """ Tarantool `datetime`_ extension type implementation module. """ +# pylint: disable=line-too-long from copy import deepcopy diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index a4e29318..ee82cb2d 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,7 +1,7 @@ """ This module tests work with extended error type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,line-too-long import sys import unittest From 141354f8a46ac81bbaba3577d6cae380da3fdac4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 18:59:39 +0300 Subject: [PATCH 114/190] pylint: disable duplicate-code cases in code Disable all cases of R0801 duplicate-code in code. ConnectionPool, MeshConnection, Connection and Tarantool server have similar API, thus they have similar init arguments. Part of #270 --- docs/source/conf.py | 2 +- tarantool/connection_pool.py | 2 +- tarantool/mesh_connection.py | 1 + test/suites/lib/tarantool_server.py | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index d6050c94..e5ca7b8f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,7 +10,7 @@ All configuration values have a default; values that are commented out serve to show the default. """ -# pylint: disable=invalid-name,redefined-builtin +# pylint: disable=invalid-name,redefined-builtin,duplicate-code import tarantool diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index bd974038..77f0207b 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -370,7 +370,7 @@ class ConnectionPool(ConnectionInterface): >>> resp - ['AAAA', 'Alpha'] """ - # pylint: disable=too-many-public-methods + # pylint: disable=too-many-public-methods,duplicate-code def __init__(self, addrs, diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 58f95e2c..f0dce29b 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -267,6 +267,7 @@ class MeshConnection(Connection): """ Represents a connection to a cluster of Tarantool servers. """ + # pylint: disable=duplicate-code def __init__(self, host=None, port=None, user=None, diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 012f5d35..eacf5323 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -55,7 +55,7 @@ class TarantoolServer(): """ Class to start up a new Tarantool server. """ - # pylint: disable=too-many-instance-attributes,too-many-arguments + # pylint: disable=too-many-instance-attributes,too-many-arguments,duplicate-code default_tarantool = { "bin": "tarantool", From ce056e53b470c28bb62a80fa093985a4d988027e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 29 Mar 2023 19:00:40 +0300 Subject: [PATCH 115/190] pylint: disable duplicate-code cases in tests Disable all cases of R0801 duplicate-code in tests. it may be for the best to remove instance setup to some common place, but for now it doesn't seems like it's worth the effort. Part of #270 --- test/suites/test_connection.py | 2 +- test/suites/test_datetime.py | 2 +- test/suites/test_decimal.py | 2 +- test/suites/test_dml.py | 2 +- test/suites/test_error_ext.py | 2 +- test/suites/test_execute.py | 2 +- test/suites/test_interval.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_protocol.py | 2 +- test/suites/test_push.py | 2 +- test/suites/test_uuid.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index ee619715..52b3ebf0 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -1,7 +1,7 @@ """ This module tests basic connection behavior. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index e429553b..c4278120 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -1,7 +1,7 @@ """ This module tests work with datetime type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,too-many-function-args +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,too-many-function-args,duplicate-code import sys import re diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 01c85ad3..9d7d8211 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -1,7 +1,7 @@ """ This module tests work with decimal type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 58048895..bf0d8f03 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -1,7 +1,7 @@ """ This module tests basic data operations. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme,too-many-public-methods +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,fixme,too-many-public-methods,duplicate-code import sys import unittest diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index ee82cb2d..373ea1b8 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -1,7 +1,7 @@ """ This module tests work with extended error type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,line-too-long +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,line-too-long,duplicate-code import sys import unittest diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 2d6d0478..3450df3c 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -1,7 +1,7 @@ """ This module tests API fo execute SQL on a server. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index dc6ffc36..d6508925 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -1,7 +1,7 @@ """ This module tests work with datetime interval type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,too-many-function-args +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,too-many-function-args,duplicate-code import re import sys diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 6bd53c23..10cc5899 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code import sys import time diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 9aef72b2..b6be1ae9 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -1,7 +1,7 @@ """ This module tests connection authentication. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access +# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,duplicate-code import sys import unittest diff --git a/test/suites/test_push.py b/test/suites/test_push.py index ed44bd21..0a19794a 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -1,7 +1,7 @@ """ This module tests work with IPROTO pushed messages. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 52ab5bda..f76665c0 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -1,7 +1,7 @@ """ This module tests work with UUID type. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest From 79cc160b2700a32cd569fb53887aeee644cc1275 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:03:29 +0300 Subject: [PATCH 116/190] pylint: disable no-self-use cases Disable all cases of R0201 no-self-use in code. We don't plan to change API yet. Part of #270 --- tarantool/connection.py | 2 +- tarantool/connection_pool.py | 2 +- test/setup_command.py | 1 + test/suites/test_pool.py | 2 +- test/suites/test_ssl.py | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index cbe075a4..2663d92d 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -489,7 +489,7 @@ class Connection(ConnectionInterface): check its status, call procedures and evaluate Lua code on server, make simple data manipulations and execute SQL queries. """ - # pylint: disable=too-many-instance-attributes,too-many-public-methods + # pylint: disable=too-many-instance-attributes,too-many-public-methods,no-self-use # DBAPI Extension: supply exceptions as attributes on the connection Error = Error diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 77f0207b..d133312f 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -370,7 +370,7 @@ class ConnectionPool(ConnectionInterface): >>> resp - ['AAAA', 'Alpha'] """ - # pylint: disable=too-many-public-methods,duplicate-code + # pylint: disable=too-many-public-methods,duplicate-code,no-self-use def __init__(self, addrs, diff --git a/test/setup_command.py b/test/setup_command.py index 517e0e06..14975666 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -17,6 +17,7 @@ class Test(setuptools.Command): """ Class implementing `python setup.py test`. """ + # pylint: disable=no-self-use user_options = [] description = 'Run tests' diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 10cc5899..a9918f8d 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code,no-self-use import sys import time diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 99707f6a..2a1c7ca2 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,7 +1,7 @@ """ This module tests connection through SSL. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches,too-many-locals +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches,too-many-locals,no-self-use import os import sys From 471cefc4ed585e19135395d08411b539f76c9c24 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:04:15 +0300 Subject: [PATCH 117/190] pylint: compatibility workaround Allow lint to not fail for all version from Python 3.6 latest one to python 3.10 latest one. See also [1]. 1. https://github.com/pylint-dev/pylint/issues/3312 Part of #270 --- .pylintrc | 4 ++++ tarantool/connection.py | 8 ++++---- tarantool/connection_pool.py | 4 ++-- test/setup_command.py | 2 +- test/suites/test_encoding.py | 2 +- test/suites/test_pool.py | 4 ++-- test/suites/test_ssl.py | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.pylintrc b/.pylintrc index 3fd8b942..f3baa9b8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,7 @@ +[MAIN] +# Enable backward compatibility with pylint for pylint down to Python 3.6 one +ignore=bad-option-value + [BASIC] # Good variable names which should always be accepted, separated by a comma diff --git a/tarantool/connection.py b/tarantool/connection.py index 2663d92d..4324d419 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -489,7 +489,7 @@ class Connection(ConnectionInterface): check its status, call procedures and evaluate Lua code on server, make simple data manipulations and execute SQL queries. """ - # pylint: disable=too-many-instance-attributes,too-many-public-methods,no-self-use + # pylint: disable=too-many-instance-attributes,too-many-public-methods,bad-option-value,no-self-use # DBAPI Extension: supply exceptions as attributes on the connection Error = Error @@ -1003,7 +1003,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=self.ssl_password) return - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) @@ -1015,7 +1015,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=line.rstrip()) return - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) @@ -1029,7 +1029,7 @@ def password_raise_error(): password=password_raise_error) return - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) raise SslError(exc_list) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index d133312f..6b34f313 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -370,7 +370,7 @@ class ConnectionPool(ConnectionInterface): >>> resp - ['AAAA', 'Alpha'] """ - # pylint: disable=too-many-public-methods,duplicate-code,no-self-use + # pylint: disable=too-many-public-methods,duplicate-code,bad-option-value,no-self-use,super-init-not-called,bad-option-value def __init__(self, addrs, @@ -648,7 +648,7 @@ def _request_process_loop(self, key, unit, last_refresh): method = getattr(Connection, task.method_name) try: resp = method(unit.conn, *task.args, **task.kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except unit.output_queue.put(exc) else: unit.output_queue.put(resp) diff --git a/test/setup_command.py b/test/setup_command.py index 14975666..517c9109 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -17,7 +17,7 @@ class Test(setuptools.Command): """ Class implementing `python setup.py test`. """ - # pylint: disable=no-self-use + # pylint: disable=bad-option-value,no-self-use user_options = [] description = 'Run tests' diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index cbaf0bda..7149b43c 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -73,7 +73,7 @@ def setUpClass(cls): def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except self.fail(f'Function raised Exception: {repr(exc)}') def setUp(self): diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index a9918f8d..2f9c855a 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code,no-self-use +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code,bad-option-value,no-self-use import sys import time @@ -69,7 +69,7 @@ def retry(self, func, count=5, timeout=0.5): for i in range(count): try: func() - except Exception as exc: # pylint: disable=broad-exception-caught + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except if i + 1 == count: raise exc diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 2a1c7ca2..d6c6ae2d 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -1,7 +1,7 @@ """ This module tests connection through SSL. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches,too-many-locals,no-self-use +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-branches,too-many-locals,bad-option-value,no-self-use import os import sys From ad8ad7d7fb651ff634c9373135dc9a7bb70ca52e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 12:42:33 +0300 Subject: [PATCH 118/190] test: add flake8 dependency See [1]. 1. https://pypi.org/project/pylint/ --- requirements-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-test.txt b/requirements-test.txt index f73f4d4b..8b3b9693 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -2,3 +2,4 @@ git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-complianc pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' pylint +flake8 From e9898a142e7a7e15ca91bf31214c8085f25701c9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 12:43:17 +0300 Subject: [PATCH 119/190] make: additional lint with flake8 Part of #270 --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f317cb8c..06d78a96 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ PYTHON_FILES=tarantool test setup.py docs/source/conf.py .PHONY: lint lint: python3 -m pylint --recursive=y $(PYTHON_FILES) + python3 -m flake8 $(PYTHON_FILES) .PHONY: test From aaba232368885fee0ebe22fef8077878526fd2da Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 12:50:50 +0300 Subject: [PATCH 120/190] flake8: ignore line too long cases Ignore E501 line too long since it's already checked with pylint. It seems impossible to transfer its rules like "skip docstring links and tables by regexp", so we just skip it here. Part of #270 --- .flake8 | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..db40f307 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +# Use pylint for lines length check +ignore=E501 From 2c31fe15d121d1a4266a71a340f1658df749a14d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 15:31:02 +0300 Subject: [PATCH 121/190] flake8: ignore rule in configuration template Ignore all cases of E265 block comment should start with '# ' in documentation configuration template. Part of #270 --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index e5ca7b8f..e9db5edb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,6 +11,7 @@ serve to show the default. """ # pylint: disable=invalid-name,redefined-builtin,duplicate-code +# flake8: noqa: E265 import tarantool From 5b8d967bd34fe5ea05f10801233e30e5fdd6f4fa Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 17:33:46 +0300 Subject: [PATCH 122/190] flake8: ignore outdated rule W503 is an outdated warning which contradicts modern W504 [1]. 1. https://www.flake8rules.com/rules/W503.html Part of #270 --- .flake8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index db40f307..4425b53d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,3 @@ [flake8] # Use pylint for lines length check -ignore=E501 +ignore=E501,W503 From f9b2136ba34db40cc107878232e96d807ab689a0 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 17:36:00 +0300 Subject: [PATCH 123/190] flake8: fix local variable unused cases Fix several cases of F841 local variable is assigned to but never used. Part of #270 --- tarantool/connection_pool.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 6b34f313..2248429d 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -569,7 +569,8 @@ def _get_new_state(self, unit): try: read_only = resp.data[0]['ro'] except (IndexError, KeyError) as exc: - msg = f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + msg = (f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) @@ -581,7 +582,8 @@ def _get_new_state(self, unit): warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) except (IndexError, KeyError) as exc: - msg = f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + msg = (f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) From e4740c3a271e9ee03a700ca7b9eb61f139239bff Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 12:56:15 +0300 Subject: [PATCH 124/190] flake8: fix style This patch includes fixes for all remaining cases of - E101 indentation contains mixed spaces and tabs, - E121 continuation line under-indented for hanging indent, - E122 continuation line missing indentation or outdented, - E123 closing bracket does not match indentation of opening bracket's line, - E124 closing bracket does not match visual indentation, - E126 continuation line over-indented for hanging indent, - E127 continuation line over-indented for visual indent, - E128 continuation line under-indented for visual indent, - E131 continuation line unaligned for hanging indent, - E201 whitespace after '(', - E202 whitespace before '(', - E203 Whitespace before ':', - E221 multiple spaces before operator, - E225 missing whitespace around operator, - E226 missing whitespace around arithmetic operator, - E227 missing whitespace around bitwise or shift, operator, - E231 missing whitespace after ',', ';', or ':', - E241 multiple spaces after ',', - E251 unexpected spaces around keyword / parameter equals, - E252 missing whitespace around parameter equals, - E261 at least two spaces before inline comment, - E275 missing whitespace after keyword, - E301 expected 1 blank line, - E302 expected 2 blank lines, - E303 too many blank lines, - E305 expected 2 blank lines after class or function definition, - E306 expected 1 blank line before a nested definition, - E502 the backslash is redundant between brackets, - W191 indentation contains tabs, - W293 blank line contains whitespace, - W504 line break after binary operator. Part of #270 --- setup.py | 3 + tarantool/connection.py | 157 +- tarantool/connection_pool.py | 24 +- tarantool/crud.py | 4 +- tarantool/dbapi.py | 2 +- tarantool/error.py | 8 + tarantool/mesh_connection.py | 2 +- tarantool/msgpack_ext/datetime.py | 16 +- tarantool/msgpack_ext/decimal.py | 16 +- tarantool/msgpack_ext/error.py | 2 + tarantool/msgpack_ext/interval.py | 2 + tarantool/msgpack_ext/packer.py | 7 +- tarantool/msgpack_ext/types/datetime.py | 45 +- tarantool/msgpack_ext/types/interval.py | 34 +- .../msgpack_ext/types/timezones/__init__.py | 8 +- .../types/timezones/gen-timezones.sh | 28 +- .../msgpack_ext/types/timezones/timezones.py | 3544 ++++++++--------- tarantool/msgpack_ext/unpacker.py | 7 +- tarantool/msgpack_ext/uuid.py | 2 + tarantool/request.py | 4 + tarantool/response.py | 9 +- tarantool/schema.py | 3 +- tarantool/types.py | 7 +- tarantool/utils.py | 4 + test/setup_command.py | 1 + test/suites/__init__.py | 4 +- test/suites/lib/skip.py | 14 +- test/suites/lib/tarantool_server.py | 19 +- test/suites/test_connection.py | 2 +- test/suites/test_crud.py | 169 +- test/suites/test_datetime.py | 35 +- test/suites/test_dbapi.py | 20 +- test/suites/test_decimal.py | 33 +- test/suites/test_dml.py | 147 +- test/suites/test_encoding.py | 7 +- test/suites/test_error_ext.py | 121 +- test/suites/test_interval.py | 41 +- test/suites/test_mesh.py | 2 +- test/suites/test_package.py | 1 - test/suites/test_pool.py | 47 +- test/suites/test_protocol.py | 1 + test/suites/test_push.py | 6 +- test/suites/test_schema.py | 43 +- test/suites/test_ssl.py | 27 +- test/suites/test_uuid.py | 4 +- 45 files changed, 2340 insertions(+), 2342 deletions(-) diff --git a/setup.py b/setup.py index 4473f52e..bd2a697c 100755 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ cmdclass = {} command_options = {} + class BuildPyCommand(build_py): """ Build the package @@ -38,6 +39,7 @@ def run(self): return super().run() + cmdclass["build_py"] = BuildPyCommand # Build Sphinx documentation (html) @@ -68,6 +70,7 @@ def read(*parts): with codecs.open(filename, encoding='utf-8') as file: return file.read() + def get_dependencies(filename): """ Get package dependencies from the `requirements.txt`. diff --git a/tarantool/connection.py b/tarantool/connection.py index 4324d419..9551b313 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -112,6 +112,7 @@ WWSAEWOULDBLOCK = 10035 ER_UNKNOWN_REQUEST_TYPE = 48 + # Based on https://realpython.com/python-interface/ class ConnectionInterface(metaclass=abc.ABCMeta): """ @@ -126,33 +127,33 @@ class ConnectionInterface(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, subclass): - return (hasattr(subclass, 'close') and - callable(subclass.close) and - hasattr(subclass, 'is_closed') and - callable(subclass.is_closed) and - hasattr(subclass, 'connect') and - callable(subclass.connect) and - hasattr(subclass, 'call') and - callable(subclass.call) and - hasattr(subclass, 'eval') and - callable(subclass.eval) and - hasattr(subclass, 'replace') and - callable(subclass.replace) and - hasattr(subclass, 'insert') and - callable(subclass.insert) and - hasattr(subclass, 'delete') and - callable(subclass.delete) and - hasattr(subclass, 'upsert') and - callable(subclass.upsert) and - hasattr(subclass, 'update') and - callable(subclass.update) and - hasattr(subclass, 'ping') and - callable(subclass.ping) and - hasattr(subclass, 'select') and - callable(subclass.select) and - hasattr(subclass, 'execute') and - callable(subclass.execute) or - NotImplemented) + return (hasattr(subclass, 'close') + and callable(subclass.close) + and hasattr(subclass, 'is_closed') + and callable(subclass.is_closed) + and hasattr(subclass, 'connect') + and callable(subclass.connect) + and hasattr(subclass, 'call') + and callable(subclass.call) + and hasattr(subclass, 'eval') + and callable(subclass.eval) + and hasattr(subclass, 'replace') + and callable(subclass.replace) + and hasattr(subclass, 'insert') + and callable(subclass.insert) + and hasattr(subclass, 'delete') + and callable(subclass.delete) + and hasattr(subclass, 'upsert') + and callable(subclass.upsert) + and hasattr(subclass, 'update') + and callable(subclass.update) + and hasattr(subclass, 'ping') + and callable(subclass.ping) + and hasattr(subclass, 'select') + and callable(subclass.select) + and hasattr(subclass, 'execute') + and callable(subclass.execute) + or NotImplemented) @abc.abstractmethod def close(self): @@ -481,6 +482,7 @@ class JoinState(Enum): No more messages expected. """ + class Connection(ConnectionInterface): """ Represents a connection to the Tarantool server. @@ -791,8 +793,8 @@ def __init__(self, host, port, # pylint: disable=too-many-arguments,too-many-locals if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'): - raise ConfigurationError("msgpack>=1.0.0 only supports None and " + - "'utf-8' encoding option values") + raise ConfigurationError("msgpack>=1.0.0 only supports None and " + + "'utf-8' encoding option values") if os.name == 'nt': libc = ctypes.WinDLL( @@ -939,12 +941,12 @@ def wrap_socket_ssl(self): ver = sys.version_info if ver[0] < 3 or (ver[0] == 3 and ver[1] < 5): - raise SslError("SSL transport is supported only since " + + raise SslError("SSL transport is supported only since " "python 3.5") if ((self.ssl_cert_file is None and self.ssl_key_file is not None) or (self.ssl_cert_file is not None and self.ssl_key_file is None)): - raise SslError("Both ssl_cert_file and ssl_key_file should be " + + raise SslError("Both ssl_cert_file and ssl_key_file should be " "configured or unconfigured") try: @@ -1003,10 +1005,9 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=self.ssl_password) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) - if self.ssl_password_file is not None: with open(self.ssl_password_file, encoding=self.encoding) as file: for line in file: @@ -1015,21 +1016,20 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=line.rstrip()) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) - try: def password_raise_error(): - raise SslError("Password prompt for decrypting the private " + - "key is unsupported, use ssl_password or " + + raise SslError("Password prompt for decrypting the private " + "key is unsupported, use ssl_password or " "ssl_password_file") context.load_cert_chain(certfile=self.ssl_cert_file, keyfile=self.ssl_key_file, password=password_raise_error) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except exc_list.append(exc) raise SslError(exc_list) @@ -1216,7 +1216,7 @@ def check(): # Check that connection is alive retbytes = self._sys_recv(sock_fd, buf, 1, flag) err = 0 - if os.name!= 'nt': + if os.name != 'nt': err = ctypes.get_errno() else: err = ctypes.get_last_error() @@ -1335,7 +1335,7 @@ def _schemaful_connection_check(self): :raise: :exc:`~tarantool.error.NotSupportedError` """ if self.schema is None: - raise NotSupportedError('This method is not available in ' + + 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): @@ -1477,11 +1477,12 @@ def authenticate(self, user, password): if not self._socket: return self._opt_reconnect() - request = RequestAuthenticate(self, - salt=self._salt, - user=self.user, - password=self.password, - auth_type=self._get_auth_type()) + request = RequestAuthenticate( + self, + salt=self._salt, + user=self.user, + password=self.password, + auth_type=self._get_auth_type()) auth_response = self._send_request_wo_reconnect(request) if auth_response.return_code == 0 and self.schema is not None: self.flush_schema() @@ -1503,13 +1504,13 @@ def _get_auth_type(self): auth_type = AUTH_TYPE_CHAP_SHA1 else: if self._server_auth_type not in AUTH_TYPES: - raise ConfigurationError('Unknown server authentication type ' + - str(self._server_auth_type)) + raise ConfigurationError('Unknown server authentication type ' + + str(self._server_auth_type)) auth_type = self._server_auth_type else: if self._client_auth_type not in AUTH_TYPES: - raise ConfigurationError('Unknown client authentication type ' + - str(self._client_auth_type)) + raise ConfigurationError('Unknown client authentication type ' + + str(self._client_auth_type)) auth_type = self._client_auth_type if auth_type == AUTH_TYPE_PAP_SHA256 and self.transport != SSL_TRANSPORT: @@ -2054,8 +2055,8 @@ def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, i if iterator is None: iterator = ITERATOR_EQ - if key is None or (isinstance(key, (list, tuple)) and - len(key) == 0): + if key is None or (isinstance(key, (list, tuple)) + and len(key) == 0): iterator = ITERATOR_ALL # Perform smart type checking (scalar / list of scalars / list of @@ -2203,7 +2204,7 @@ def _unpacker_factory(self): return self._unpacker_factory_impl(self) def crud_insert(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Inserts row through the `crud `__. @@ -2237,7 +2238,7 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], return CrudResult(crud_resp[0]) def crud_insert_object(self, space_name: str, values: dict, - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Inserts object row through the `crud `__. @@ -2271,7 +2272,7 @@ def crud_insert_object(self, space_name: str, values: dict, return CrudResult(crud_resp[0]) def crud_insert_many(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Inserts batch rows through the `crud `__. @@ -2312,7 +2313,7 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], return res def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Inserts batch object rows through the `crud `__. @@ -2352,7 +2353,7 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], return res - def crud_get(self, space_name: str, key: int, opts: Optional[dict]=None) -> CrudResult: + def crud_get(self, space_name: str, key: int, opts: Optional[dict] = None) -> CrudResult: """ Gets row through the `crud `__. @@ -2384,8 +2385,8 @@ def crud_get(self, space_name: str, key: int, opts: Optional[dict]=None) -> Crud return CrudResult(crud_resp[0]) - def crud_update(self, space_name: str, key: int, operations: Optional[list]=None, - opts: Optional[dict]=None) -> CrudResult: + def crud_update(self, space_name: str, key: int, operations: Optional[list] = None, + opts: Optional[dict] = None) -> CrudResult: """ Updates row through the `crud `__. @@ -2423,7 +2424,7 @@ def crud_update(self, space_name: str, key: int, operations: Optional[list]=None return CrudResult(crud_resp[0]) - def crud_delete(self, space_name: str, key: int, opts: Optional[dict]=None) -> CrudResult: + def crud_delete(self, space_name: str, key: int, opts: Optional[dict] = None) -> CrudResult: """ Deletes row through the `crud `__. @@ -2456,7 +2457,7 @@ def crud_delete(self, space_name: str, key: int, opts: Optional[dict]=None) -> C return CrudResult(crud_resp[0]) def crud_replace(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Replaces row through the `crud `__. @@ -2490,7 +2491,7 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], return CrudResult(crud_resp[0]) def crud_replace_object(self, space_name: str, values: dict, - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Replaces object row through the `crud `__. @@ -2524,7 +2525,7 @@ def crud_replace_object(self, space_name: str, values: dict, return CrudResult(crud_resp[0]) def crud_replace_many(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Replaces batch rows through the `crud `__. @@ -2565,7 +2566,7 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], return res def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Replaces batch object rows through the `crud `__. @@ -2606,7 +2607,7 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], return res def crud_upsert(self, space_name: str, values: Union[tuple, list], - operations: Optional[list]=None, opts: Optional[dict]=None) -> CrudResult: + operations: Optional[list] = None, opts: Optional[dict] = None) -> CrudResult: """ Upserts row through the `crud `__. @@ -2646,8 +2647,8 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], return CrudResult(crud_resp[0]) def crud_upsert_object(self, space_name: str, values: dict, - operations: Optional[list]=None, - opts: Optional[dict]=None) -> CrudResult: + operations: Optional[list] = None, + opts: Optional[dict] = None) -> CrudResult: """ Upserts object row through the `crud `__. @@ -2687,7 +2688,7 @@ def crud_upsert_object(self, space_name: str, values: dict, return CrudResult(crud_resp[0]) def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Upserts batch rows through the `crud `__. @@ -2728,7 +2729,7 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] return res def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple, list], - opts: Optional[dict]=None) -> CrudResult: + opts: Optional[dict] = None) -> CrudResult: """ Upserts batch object rows through the `crud `__. @@ -2768,8 +2769,8 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple return res - def crud_select(self, space_name: str, conditions: Optional[list]=None, - opts: Optional[dict]=None) -> CrudResult: + def crud_select(self, space_name: str, conditions: Optional[list] = None, + opts: Optional[dict] = None) -> CrudResult: """ Selects rows through the `crud `__. @@ -2804,7 +2805,7 @@ def crud_select(self, space_name: str, conditions: Optional[list]=None, return CrudResult(crud_resp[0]) - def crud_min(self, space_name: str, index_name: str, opts: Optional[dict]=None) -> CrudResult: + def crud_min(self, space_name: str, index_name: str, opts: Optional[dict] = None) -> CrudResult: """ Gets rows with minimum value in the specified index through the `crud `__. @@ -2836,7 +2837,7 @@ def crud_min(self, space_name: str, index_name: str, opts: Optional[dict]=None) return CrudResult(crud_resp[0]) - def crud_max(self, space_name: str, index_name: str, opts: Optional[dict]=None) -> CrudResult: + def crud_max(self, space_name: str, index_name: str, opts: Optional[dict] = None) -> CrudResult: """ Gets rows with maximum value in the specified index through the `crud `__. @@ -2868,7 +2869,7 @@ def crud_max(self, space_name: str, index_name: str, opts: Optional[dict]=None) return CrudResult(crud_resp[0]) - def crud_truncate(self, space_name: str, opts: Optional[dict]=None) -> bool: + def crud_truncate(self, space_name: str, opts: Optional[dict] = None) -> bool: """ Truncate rows through the `crud `__. @@ -2899,7 +2900,7 @@ def crud_truncate(self, space_name: str, opts: Optional[dict]=None) -> bool: return crud_resp[0] - def crud_len(self, space_name: str, opts: Optional[dict]=None) -> int: + def crud_len(self, space_name: str, opts: Optional[dict] = None) -> int: """ Gets the number of tuples in the space through the `crud `__. @@ -2930,7 +2931,7 @@ def crud_len(self, space_name: str, opts: Optional[dict]=None) -> int: return crud_resp[0] - def crud_storage_info(self, opts: Optional[dict]=None) -> dict: + def crud_storage_info(self, opts: Optional[dict] = None) -> dict: """ Gets storages status through the `crud `__. @@ -2957,8 +2958,8 @@ def crud_storage_info(self, opts: Optional[dict]=None) -> dict: return crud_resp[0] - def crud_count(self, space_name: str, conditions: Optional[list]=None, - opts: Optional[dict]=None) -> int: + def crud_count(self, space_name: str, conditions: Optional[list] = None, + opts: Optional[dict] = None) -> int: """ Gets rows count through the `crud `__. @@ -2993,7 +2994,7 @@ def crud_count(self, space_name: str, conditions: Optional[list]=None, return crud_resp[0] - def crud_stats(self, space_name: str=None) -> CrudResult: + def crud_stats(self, space_name: str = None) -> CrudResult: """ Gets statistics from the `crud `__. diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 2248429d..508460d1 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -161,6 +161,7 @@ class PoolUnit(): :type: :obj:`bool` """ + # Based on https://realpython.com/python-interface/ class StrategyInterface(metaclass=abc.ABCMeta): """ @@ -169,13 +170,13 @@ class StrategyInterface(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, subclass): - return (hasattr(subclass, '__init__') and - callable(subclass.__init__) and - hasattr(subclass, 'update') and - callable(subclass.update) and - hasattr(subclass, 'getnext') and - callable(subclass.getnext) or - NotImplemented) + return (hasattr(subclass, '__init__') + and callable(subclass.__init__) + and hasattr(subclass, 'update') + and callable(subclass.update) + and hasattr(subclass, 'getnext') + and callable(subclass.getnext) + or NotImplemented) @abc.abstractmethod def __init__(self, pool): @@ -204,6 +205,7 @@ def getnext(self, mode): raise NotImplementedError + class RoundRobinStrategy(StrategyInterface): """ Simple round-robin pool servers rotation. @@ -500,7 +502,7 @@ def __init__(self, socket_timeout=socket_timeout, reconnect_max_attempts=reconnect_max_attempts, reconnect_delay=reconnect_delay, - connect_now=False, # Connect in ConnectionPool.connect() + connect_now=False, # Connect in ConnectionPool.connect() encoding=encoding, call_16=call_16, connection_timeout=connection_timeout, @@ -650,7 +652,7 @@ def _request_process_loop(self, key, unit, last_refresh): method = getattr(Connection, task.method_name) try: resp = method(unit.conn, *task.args, **task.kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except unit.output_queue.put(exc) else: unit.output_queue.put(resp) @@ -918,7 +920,7 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, mode=Mode.RW, """ return self._send(mode, 'upsert', space_name, tuple_value, - op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) + op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, on_push=None, on_push_ctx=None): @@ -955,7 +957,7 @@ def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, """ return self._send(mode, 'update', space_name, key, - op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) + op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) def ping(self, notime=False, *, mode=None): """ diff --git a/tarantool/crud.py b/tarantool/crud.py index 7850d164..d10726c5 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -34,14 +34,14 @@ def __init__(self, response): raise RuntimeError('Unable to decode response to object due to unknown type') -class CrudResult(CrudResponse): # pylint: disable=too-few-public-methods +class CrudResult(CrudResponse): # pylint: disable=too-few-public-methods """ Contains result's fields from result variable of crud module operation. """ -class CrudError(CrudResponse): # pylint: disable=too-few-public-methods +class CrudError(CrudResponse): # pylint: disable=too-few-public-methods """ Contains error's fields from error variable of crud module operation. diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index e94a45de..908355fc 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -17,7 +17,7 @@ NotSupportedError, ) -Warning = Warning # pylint: disable=redefined-builtin,self-assigning-variable +Warning = Warning # pylint: disable=redefined-builtin,self-assigning-variable # pylint: disable=invalid-name paramstyle = 'named' diff --git a/tarantool/error.py b/tarantool/error.py index 6ad771de..85e30c03 100644 --- a/tarantool/error.py +++ b/tarantool/error.py @@ -111,6 +111,7 @@ class ConfigurationError(Error): Error of initialization with a user-provided configuration. """ + class MsgpackError(Error): """ Error with encoding or decoding of `MP_EXT`_ types. @@ -118,11 +119,13 @@ class MsgpackError(Error): .. _MP_EXT: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/ """ + class MsgpackWarning(UserWarning): """ Warning with encoding or decoding of `MP_EXT`_ types. """ + # Monkey patch os.strerror for win32 if sys.platform == "win32": # Windows Sockets Error Codes (not all, but related on network errors) @@ -267,6 +270,7 @@ class NetworkWarning(UserWarning): Warning related to network. """ + class SslError(DatabaseError): """ Error related to SSL. @@ -298,17 +302,20 @@ class ClusterDiscoveryWarning(UserWarning): Warning related to cluster discovery. """ + class ClusterConnectWarning(UserWarning): """ Warning related to cluster pool connection. """ + class PoolTolopogyWarning(UserWarning): """ Warning related to unsatisfying `box.info.ro`_ state of pool instances. """ + class PoolTolopogyError(DatabaseError): """ Exception raised due to unsatisfying `box.info.ro`_ state of @@ -317,6 +324,7 @@ class PoolTolopogyError(DatabaseError): .. _box.info.ro: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_info/ """ + class CrudModuleError(DatabaseError): """ Exception raised for errors that are related to diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index f0dce29b..0744f2f4 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -155,7 +155,7 @@ def format_error(address, err): 'port must be an int for an inet result') if result['port'] < 1 or result['port'] > 65535: return format_error(result, 'port must be in range [1, 65535] ' - 'for an inet result') + 'for an inet result') # Looks okay. return result, None diff --git a/tarantool/msgpack_ext/datetime.py b/tarantool/msgpack_ext/datetime.py index 7b78ac6c..f7323c5f 100644 --- a/tarantool/msgpack_ext/datetime.py +++ b/tarantool/msgpack_ext/datetime.py @@ -53,10 +53,10 @@ BYTEORDER = 'little' -SECONDS_SIZE_BYTES = 8 -NSEC_SIZE_BYTES = 4 +SECONDS_SIZE_BYTES = 8 +NSEC_SIZE_BYTES = 4 TZOFFSET_SIZE_BYTES = 2 -TZINDEX_SIZE_BYTES = 2 +TZINDEX_SIZE_BYTES = 2 def get_int_as_bytes(data, size): @@ -77,6 +77,7 @@ def get_int_as_bytes(data, size): return data.to_bytes(size, byteorder=BYTEORDER, signed=True) + def encode(obj, _): """ Encode a datetime object. @@ -133,6 +134,7 @@ def get_bytes_as_int(data, cursor, size): part = data[cursor:cursor + size] return int.from_bytes(part, BYTEORDER, signed=True), cursor + size + def decode(data, _): """ Decode a datetime object. @@ -151,11 +153,11 @@ def decode(data, _): seconds, cursor = get_bytes_as_int(data, cursor, SECONDS_SIZE_BYTES) data_len = len(data) - if data_len == (SECONDS_SIZE_BYTES + NSEC_SIZE_BYTES + \ - TZOFFSET_SIZE_BYTES + TZINDEX_SIZE_BYTES): - nsec, cursor = get_bytes_as_int(data, cursor, NSEC_SIZE_BYTES) + if data_len == (SECONDS_SIZE_BYTES + NSEC_SIZE_BYTES + + TZOFFSET_SIZE_BYTES + TZINDEX_SIZE_BYTES): + nsec, cursor = get_bytes_as_int(data, cursor, NSEC_SIZE_BYTES) tzoffset, cursor = get_bytes_as_int(data, cursor, TZOFFSET_SIZE_BYTES) - tzindex, cursor = get_bytes_as_int(data, cursor, TZINDEX_SIZE_BYTES) + tzindex, cursor = get_bytes_as_int(data, cursor, TZINDEX_SIZE_BYTES) elif data_len == SECONDS_SIZE_BYTES: nsec = 0 tzoffset = 0 diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index c53cd73b..545ee07d 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -65,6 +65,7 @@ TARANTOOL_DECIMAL_MAX_DIGITS = 38 + def get_mp_sign(sign): """ Parse decimal sign to a nibble. @@ -88,6 +89,7 @@ def get_mp_sign(sign): raise RuntimeError + def add_mp_digit(digit, bytes_reverted, digit_count): """ Append decimal digit to a binary data array. @@ -109,6 +111,7 @@ def add_mp_digit(digit, bytes_reverted, digit_count): else: bytes_reverted.append(digit) + def check_valid_tarantool_decimal(str_repr, scale, first_digit_ind): """ Decimal numbers have 38 digits of precision, that is, the total @@ -183,21 +186,21 @@ def check_valid_tarantool_decimal(str_repr, scale, first_digit_ind): return True if (digit_count - scale) > TARANTOOL_DECIMAL_MAX_DIGITS: - raise MsgpackError('Decimal cannot be encoded: Tarantool decimal ' + \ + raise MsgpackError('Decimal cannot be encoded: Tarantool decimal ' 'supports a maximum of 38 digits.') starts_with_zero = str_repr[first_digit_ind] == '0' - if ( (digit_count > TARANTOOL_DECIMAL_MAX_DIGITS + 1) or \ - (digit_count == TARANTOOL_DECIMAL_MAX_DIGITS + 1 \ - and not starts_with_zero)): - warn('Decimal encoded with loss of precision: ' + \ + if (digit_count > TARANTOOL_DECIMAL_MAX_DIGITS + 1) or \ + (digit_count == TARANTOOL_DECIMAL_MAX_DIGITS + 1 and not starts_with_zero): + warn('Decimal encoded with loss of precision: ' 'Tarantool decimal supports a maximum of 38 digits.', MsgpackWarning) return False return True + def strip_decimal_str(str_repr, scale, first_digit_ind): """ Strip decimal digits after the decimal point if decimal cannot be @@ -225,6 +228,7 @@ def strip_decimal_str(str_repr, scale, first_digit_ind): # Do not strips zeroes before the decimal point return str_repr + def encode(obj, _): """ Encode a decimal object. @@ -308,6 +312,7 @@ def get_str_sign(nibble): raise MsgpackError('Unexpected MP_DECIMAL sign nibble') + def add_str_digit(digit, digits_reverted, scale): """ Append decimal digit to a binary data array. @@ -334,6 +339,7 @@ def add_str_digit(digit, digits_reverted, scale): digits_reverted.append(str(digit)) + def decode(data, _): """ Decode a decimal object. diff --git a/tarantool/msgpack_ext/error.py b/tarantool/msgpack_ext/error.py index a3f13a04..ef322d58 100644 --- a/tarantool/msgpack_ext/error.py +++ b/tarantool/msgpack_ext/error.py @@ -16,6 +16,7 @@ `error`_ type id. """ + def encode(obj, packer): """ Encode an error object. @@ -33,6 +34,7 @@ def encode(obj, packer): err_map = encode_box_error(obj) return packer.pack(err_map) + def decode(data, unpacker): """ Decode an error object. diff --git a/tarantool/msgpack_ext/interval.py b/tarantool/msgpack_ext/interval.py index 01d35ea4..12136772 100644 --- a/tarantool/msgpack_ext/interval.py +++ b/tarantool/msgpack_ext/interval.py @@ -51,6 +51,7 @@ `datetime.interval`_ type id. """ + def encode(obj, _): """ Encode an interval object. @@ -79,6 +80,7 @@ def encode(obj, _): return buf + def decode(data, unpacker): """ Decode an interval object. diff --git a/tarantool/msgpack_ext/packer.py b/tarantool/msgpack_ext/packer.py index ebafb10c..8782474e 100644 --- a/tarantool/msgpack_ext/packer.py +++ b/tarantool/msgpack_ext/packer.py @@ -19,13 +19,14 @@ import tarantool.msgpack_ext.interval as ext_interval encoders = [ - {'type': Decimal, 'ext': ext_decimal }, - {'type': UUID, 'ext': ext_uuid }, - {'type': BoxError, 'ext': ext_error }, + {'type': Decimal, 'ext': ext_decimal}, + {'type': UUID, 'ext': ext_uuid}, + {'type': BoxError, 'ext': ext_error}, {'type': Datetime, 'ext': ext_datetime}, {'type': Interval, 'ext': ext_interval}, ] + def default(obj, packer=None): """ :class:`msgpack.Packer` encoder. diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 2e8d8804..0ecaa4dd 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -17,6 +17,7 @@ SEC_IN_MIN = 60 MONTH_IN_YEAR = 12 + def compute_offset(timestamp): """ Compute timezone offset. Offset is computed each time and not stored @@ -42,6 +43,7 @@ def compute_offset(timestamp): # There is no precision loss since offset is in minutes return int(utc_offset.total_seconds()) // SEC_IN_MIN + def get_python_tzinfo(tz): """ All non-abbreviated Tarantool timezones are represented as pytz @@ -72,6 +74,7 @@ def get_python_tzinfo(tz): return pytz.FixedOffset(tt_tzinfo['offset']) + class Datetime(): """ Class representing Tarantool `datetime`_ info. Internals are based @@ -278,10 +281,10 @@ def __init__(self, *, timestamp=None, year=None, month=None, # https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ if timestamp is not None: # pylint: disable=too-many-boolean-expressions - if ((year is not None) or (month is not None) or \ - (day is not None) or (hour is not None) or \ - (minute is not None) or (sec is not None)): - raise ValueError('Cannot provide both timestamp and year, month, ' + + if ((year is not None) or (month is not None) + or (day is not None) or (hour is not None) + or (minute is not None) or (sec is not None)): + raise ValueError('Cannot provide both timestamp and year, month, ' 'day, hour, minute, sec') if nsec is not None: @@ -333,24 +336,24 @@ def _interval_operation(self, other, sign=1): # https://github.com/tarantool/tarantool/wiki/Datetime-Internals#date-adjustions-and-leap-years months = other.year * MONTH_IN_YEAR + other.month - res = self_dt + pandas.DateOffset(months = sign * months) + res = self_dt + pandas.DateOffset(months=sign * months) # pandas.DateOffset works exactly like Adjust.NONE if other.adjust == Adjust.EXCESS: if self_dt.day > res.day: - res = res + pandas.DateOffset(days = self_dt.day - res.day) + res = res + pandas.DateOffset(days=self_dt.day - res.day) elif other.adjust == Adjust.LAST: if self_dt.is_month_end: # day replaces days - res = res.replace(day = res.days_in_month) + res = res.replace(day=res.days_in_month) - res = res + pandas.Timedelta(weeks = sign * other.week, - days = sign * other.day, - hours = sign * other.hour, - minutes = sign * other.minute, - seconds = sign * other.sec, - microseconds = sign * (other.nsec // NSEC_IN_MKSEC), - nanoseconds = sign * (other.nsec % NSEC_IN_MKSEC)) + res = res + pandas.Timedelta(weeks=sign * other.week, + days=sign * other.day, + hours=sign * other.hour, + minutes=sign * other.minute, + seconds=sign * other.sec, + microseconds=sign * (other.nsec // NSEC_IN_MKSEC), + nanoseconds=sign * (other.nsec % NSEC_IN_MKSEC)) if res.tzinfo is not None: tzoffset = compute_offset(res) @@ -474,13 +477,13 @@ def __sub__(self, other): other_nsec = other_dt.microsecond * NSEC_IN_MKSEC + other_dt.nanosecond return Interval( - year = self_dt.year - other_dt.year, - month = self_dt.month - other_dt.month, - day = self_dt.day - other_dt.day, - hour = self_dt.hour - other_dt.hour, - minute = self_dt.minute - other_dt.minute, - sec = self_dt.second - other_dt.second, - nsec = self_nsec - other_nsec, + year=self_dt.year - other_dt.year, + month=self_dt.month - other_dt.month, + day=self_dt.day - other_dt.day, + hour=self_dt.hour - other_dt.hour, + minute=self_dt.minute - other_dt.minute, + sec=self_dt.second - other_dt.second, + nsec=self_nsec - other_nsec, ) if isinstance(other, Interval): return self._interval_operation(other, sign=-1) diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index 53aadf9a..10dc4847 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -16,6 +16,7 @@ 8: 'adjust', } + # https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.h#L34 class Adjust(Enum): """ @@ -38,6 +39,7 @@ class Adjust(Enum): Mode when day snaps to the end of month, if it happens. """ + class Interval(): """ Class representing Tarantool `datetime.interval`_ info. @@ -141,14 +143,14 @@ def __add__(self, other): # ... return Interval( - year = self.year + other.year, - month = self.month + other.month, - day = self.day + other.day, - hour = self.hour + other.hour, - minute = self.minute + other.minute, - sec = self.sec + other.sec, - nsec = self.nsec + other.nsec, - adjust = self.adjust, + year=self.year + other.year, + month=self.month + other.month, + day=self.day + other.day, + hour=self.hour + other.hour, + minute=self.minute + other.minute, + sec=self.sec + other.sec, + nsec=self.nsec + other.nsec, + adjust=self.adjust, ) def __sub__(self, other): @@ -190,14 +192,14 @@ def __sub__(self, other): # ... return Interval( - year = self.year - other.year, - month = self.month - other.month, - day = self.day - other.day, - hour = self.hour - other.hour, - minute = self.minute - other.minute, - sec = self.sec - other.sec, - nsec = self.nsec - other.nsec, - adjust = self.adjust, + year=self.year - other.year, + month=self.month - other.month, + day=self.day - other.day, + hour=self.hour - other.hour, + minute=self.minute - other.minute, + sec=self.sec - other.sec, + nsec=self.nsec - other.nsec, + adjust=self.adjust, ) def __eq__(self, other): diff --git a/tarantool/msgpack_ext/types/timezones/__init__.py b/tarantool/msgpack_ext/types/timezones/__init__.py index c0c4ce7e..47a9edf9 100644 --- a/tarantool/msgpack_ext/types/timezones/__init__.py +++ b/tarantool/msgpack_ext/types/timezones/__init__.py @@ -3,10 +3,10 @@ """ from tarantool.msgpack_ext.types.timezones.timezones import ( - TZ_AMBIGUOUS, - indexToTimezone, - timezoneToIndex, - timezoneAbbrevInfo, + TZ_AMBIGUOUS, + indexToTimezone, + timezoneToIndex, + timezoneAbbrevInfo, ) __all__ = ['TZ_AMBIGUOUS', 'indexToTimezone', 'timezoneToIndex', diff --git a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh index 161a7597..bca568b0 100755 --- a/tarantool/msgpack_ext/types/timezones/gen-timezones.sh +++ b/tarantool/msgpack_ext/types/timezones/gen-timezones.sh @@ -28,22 +28,22 @@ Tarantool timezone info. Automatically generated by """ # pylint: disable=too-many-lines -TZ_UTC = 0x01 -TZ_RFC = 0x02 -TZ_MILITARY = 0x04 +TZ_UTC = 0x01 +TZ_RFC = 0x02 +TZ_MILITARY = 0x04 TZ_AMBIGUOUS = 0x08 -TZ_NYI = 0x10 -TZ_OLSON = 0x20 -TZ_ALIAS = 0x40 -TZ_DST = 0x80 +TZ_NYI = 0x10 +TZ_OLSON = 0x20 +TZ_ALIAS = 0x40 +TZ_DST = 0x80 indexToTimezone = { EOF grep ZONE_ABBREV ${SRC_FILE} | sed "s/ZONE_ABBREV( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : %s,\n", $1, $3)}' >> ${DST_FILE} + | awk '{printf(" %s: %s,\n", $1, $3)}' >> ${DST_FILE} grep ZONE_UNIQUE ${SRC_FILE} | sed "s/ZONE_UNIQUE( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : %s,\n", $1, $2)}' >> ${DST_FILE} + | awk '{printf(" %s: %s,\n", $1, $2)}' >> ${DST_FILE} cat <> ${DST_FILE} } @@ -52,11 +52,11 @@ timezoneToIndex = { EOF grep ZONE_ABBREV ${SRC_FILE} | sed "s/ZONE_ABBREV( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : %s,\n", $3, $1)}' >> ${DST_FILE} + | awk '{printf(" %s: %s,\n", $3, $1)}' >> ${DST_FILE} grep ZONE_UNIQUE ${SRC_FILE} | sed "s/ZONE_UNIQUE( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : %s,\n", $2, $1)}' >> ${DST_FILE} + | awk '{printf(" %s: %s,\n", $2, $1)}' >> ${DST_FILE} grep ZONE_ALIAS ${SRC_FILE} | sed "s/ZONE_ALIAS( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : %s,\n", $2, $1)}' >> ${DST_FILE} + | awk '{printf(" %s: %s,\n", $2, $1)}' >> ${DST_FILE} cat <> ${DST_FILE} } @@ -64,8 +64,8 @@ cat <> ${DST_FILE} timezoneAbbrevInfo = { EOF -grep ZONE_ABBREV ${SRC_FILE} | sed "s/ZONE_ABBREV( *//g" | sed "s/[),]//g" \ - | awk '{printf("\t%s : {\"offset\" : %d, \"category\" : %s},\n", $3, $2, $4)}' >> ${DST_FILE} +grep ZONE_ABBREV ${SRC_FILE} | sed "s/ZONE_ABBREV( *//g" | sed "s/[),]//g" \ + | awk '{printf(" %s: {\"offset\": %d, \"category\": %s},\n", $3, $2, $4)}' | sed "s/|/ | /g" >> ${DST_FILE} echo "}" >> ${DST_FILE} rm timezones.h diff --git a/tarantool/msgpack_ext/types/timezones/timezones.py b/tarantool/msgpack_ext/types/timezones/timezones.py index 98d63535..082ee356 100644 --- a/tarantool/msgpack_ext/types/timezones/timezones.py +++ b/tarantool/msgpack_ext/types/timezones/timezones.py @@ -4,1785 +4,1785 @@ """ # pylint: disable=too-many-lines -TZ_UTC = 0x01 -TZ_RFC = 0x02 -TZ_MILITARY = 0x04 +TZ_UTC = 0x01 +TZ_RFC = 0x02 +TZ_MILITARY = 0x04 TZ_AMBIGUOUS = 0x08 -TZ_NYI = 0x10 -TZ_OLSON = 0x20 -TZ_ALIAS = 0x40 -TZ_DST = 0x80 +TZ_NYI = 0x10 +TZ_OLSON = 0x20 +TZ_ALIAS = 0x40 +TZ_DST = 0x80 indexToTimezone = { - 1 : "A", - 2 : "B", - 3 : "C", - 4 : "D", - 5 : "E", - 6 : "F", - 7 : "G", - 8 : "H", - 9 : "I", - 10 : "K", - 11 : "L", - 12 : "M", - 13 : "N", - 14 : "O", - 15 : "P", - 16 : "Q", - 17 : "R", - 18 : "S", - 19 : "T", - 20 : "U", - 21 : "V", - 22 : "W", - 23 : "X", - 24 : "Y", - 25 : "Z", - 32 : "AT", - 40 : "BT", - 48 : "CT", - 56 : "ET", - 64 : "GT", - 72 : "IT", - 80 : "KT", - 88 : "MT", - 96 : "PT", - 104 : "ST", - 112 : "UT", - 120 : "WT", - 128 : "ACT", - 129 : "ADT", - 130 : "AET", - 131 : "AFT", - 132 : "AMT", - 133 : "AoE", - 134 : "ART", - 135 : "AST", - 136 : "AZT", - 144 : "BDT", - 145 : "BNT", - 146 : "BOT", - 147 : "BRT", - 148 : "BST", - 149 : "BTT", - 152 : "CAT", - 153 : "CCT", - 154 : "CDT", - 155 : "CET", - 156 : "CIT", - 157 : "CKT", - 158 : "CLT", - 159 : "COT", - 160 : "CST", - 161 : "CVT", - 162 : "CXT", - 168 : "EAT", - 169 : "ECT", - 170 : "EDT", - 171 : "EET", - 172 : "EGT", - 173 : "EST", - 176 : "FET", - 177 : "FJT", - 178 : "FKT", - 179 : "FNT", - 184 : "GET", - 185 : "GFT", - 186 : "GMT", - 187 : "GST", - 188 : "GYT", - 192 : "HAA", - 193 : "HAC", - 194 : "HAE", - 195 : "HAP", - 196 : "HAR", - 197 : "HAT", - 198 : "HDT", - 199 : "HKT", - 200 : "HLV", - 201 : "HNA", - 202 : "HNC", - 203 : "HNE", - 204 : "HNP", - 205 : "HNR", - 206 : "HNT", - 207 : "HST", - 208 : "ICT", - 209 : "IDT", - 210 : "IOT", - 211 : "IST", - 216 : "JST", - 224 : "KGT", - 225 : "KIT", - 226 : "KST", - 232 : "MCK", - 233 : "MDT", - 234 : "MEZ", - 235 : "MHT", - 236 : "MMT", - 237 : "MSD", - 238 : "MSK", - 239 : "MST", - 240 : "MUT", - 241 : "MVT", - 242 : "MYT", - 248 : "NCT", - 249 : "NDT", - 250 : "NFT", - 251 : "NPT", - 252 : "NRT", - 253 : "NST", - 254 : "NUT", - 256 : "OEZ", - 264 : "PDT", - 265 : "PET", - 266 : "PGT", - 267 : "PHT", - 268 : "PKT", - 269 : "PST", - 270 : "PWT", - 271 : "PYT", - 272 : "RET", - 280 : "SBT", - 281 : "SCT", - 282 : "SGT", - 283 : "SRT", - 284 : "SST", - 288 : "TFT", - 289 : "TJT", - 290 : "TKT", - 291 : "TLT", - 292 : "TMT", - 293 : "TOT", - 294 : "TRT", - 295 : "TVT", - 296 : "UTC", - 297 : "UYT", - 298 : "UZT", - 304 : "VET", - 305 : "VUT", - 312 : "WAT", - 313 : "WDT", - 314 : "WET", - 315 : "WEZ", - 316 : "WFT", - 317 : "WGT", - 318 : "WIB", - 319 : "WIT", - 320 : "WST", - 328 : "ACDT", - 329 : "ACST", - 330 : "ADST", - 331 : "AEDT", - 332 : "AEST", - 333 : "AKDT", - 334 : "AKST", - 335 : "ALMT", - 336 : "AMDT", - 337 : "AMST", - 338 : "ANAT", - 339 : "AQTT", - 340 : "AWDT", - 341 : "AWST", - 342 : "AZOT", - 343 : "AZST", - 344 : "BDST", - 345 : "BRST", - 352 : "CAST", - 353 : "CDST", - 354 : "CEDT", - 355 : "CEST", - 356 : "CHOT", - 357 : "ChST", - 358 : "CHUT", - 359 : "CIST", - 360 : "CLDT", - 361 : "CLST", - 368 : "DAVT", - 369 : "DDUT", - 376 : "EADT", - 377 : "EAST", - 378 : "ECST", - 379 : "EDST", - 380 : "EEDT", - 381 : "EEST", - 382 : "EGST", - 384 : "FJDT", - 385 : "FJST", - 386 : "FKDT", - 387 : "FKST", - 392 : "GALT", - 393 : "GAMT", - 394 : "GILT", - 400 : "HADT", - 401 : "HAST", - 402 : "HOVT", - 408 : "IRDT", - 409 : "IRKT", - 410 : "IRST", - 416 : "KOST", - 417 : "KRAT", - 418 : "KUYT", - 424 : "LHDT", - 425 : "LHST", - 426 : "LINT", - 432 : "MAGT", - 433 : "MART", - 434 : "MAWT", - 435 : "MDST", - 436 : "MESZ", - 440 : "NFDT", - 441 : "NOVT", - 442 : "NZDT", - 443 : "NZST", - 448 : "OESZ", - 449 : "OMST", - 450 : "ORAT", - 456 : "PDST", - 457 : "PETT", - 458 : "PHOT", - 459 : "PMDT", - 460 : "PMST", - 461 : "PONT", - 462 : "PYST", - 464 : "QYZT", - 472 : "ROTT", - 480 : "SAKT", - 481 : "SAMT", - 482 : "SAST", - 483 : "SRET", - 484 : "SYOT", - 488 : "TAHT", - 489 : "TOST", - 496 : "ULAT", - 497 : "UYST", - 504 : "VLAT", - 505 : "VOST", - 512 : "WAKT", - 513 : "WAST", - 514 : "WEDT", - 515 : "WEST", - 516 : "WESZ", - 517 : "WGST", - 518 : "WITA", - 520 : "YAKT", - 521 : "YAPT", - 522 : "YEKT", - 528 : "ACWST", - 529 : "ANAST", - 530 : "AZODT", - 531 : "AZOST", - 536 : "CHADT", - 537 : "CHAST", - 538 : "CHODT", - 539 : "CHOST", - 540 : "CIDST", - 544 : "EASST", - 545 : "EFATE", - 552 : "HOVDT", - 553 : "HOVST", - 560 : "IRKST", - 568 : "KRAST", - 576 : "MAGST", - 584 : "NACDT", - 585 : "NACST", - 586 : "NAEDT", - 587 : "NAEST", - 588 : "NAMDT", - 589 : "NAMST", - 590 : "NAPDT", - 591 : "NAPST", - 592 : "NOVST", - 600 : "OMSST", - 608 : "PETST", - 616 : "SAMST", - 624 : "ULAST", - 632 : "VLAST", - 640 : "WARST", - 648 : "YAKST", - 649 : "YEKST", - 656 : "CHODST", - 664 : "HOVDST", - 672 : "Africa/Abidjan", - 673 : "Africa/Algiers", - 674 : "Africa/Bissau", - 675 : "Africa/Cairo", - 676 : "Africa/Casablanca", - 677 : "Africa/Ceuta", - 678 : "Africa/El_Aaiun", - 679 : "Africa/Johannesburg", - 680 : "Africa/Juba", - 681 : "Africa/Khartoum", - 682 : "Africa/Lagos", - 683 : "Africa/Maputo", - 684 : "Africa/Monrovia", - 685 : "Africa/Nairobi", - 686 : "Africa/Ndjamena", - 687 : "Africa/Sao_Tome", - 688 : "Africa/Tripoli", - 689 : "Africa/Tunis", - 690 : "Africa/Windhoek", - 691 : "America/Adak", - 692 : "America/Anchorage", - 693 : "America/Araguaina", - 694 : "America/Argentina/Buenos_Aires", - 695 : "America/Argentina/Catamarca", - 696 : "America/Argentina/Cordoba", - 697 : "America/Argentina/Jujuy", - 698 : "America/Argentina/La_Rioja", - 699 : "America/Argentina/Mendoza", - 700 : "America/Argentina/Rio_Gallegos", - 701 : "America/Argentina/Salta", - 702 : "America/Argentina/San_Juan", - 703 : "America/Argentina/San_Luis", - 704 : "America/Argentina/Tucuman", - 705 : "America/Argentina/Ushuaia", - 706 : "America/Asuncion", - 707 : "America/Bahia", - 708 : "America/Bahia_Banderas", - 709 : "America/Barbados", - 710 : "America/Belem", - 711 : "America/Belize", - 712 : "America/Boa_Vista", - 713 : "America/Bogota", - 714 : "America/Boise", - 715 : "America/Cambridge_Bay", - 716 : "America/Campo_Grande", - 717 : "America/Cancun", - 718 : "America/Caracas", - 719 : "America/Cayenne", - 720 : "America/Chicago", - 721 : "America/Chihuahua", - 722 : "America/Costa_Rica", - 723 : "America/Cuiaba", - 724 : "America/Danmarkshavn", - 725 : "America/Dawson", - 726 : "America/Dawson_Creek", - 727 : "America/Denver", - 728 : "America/Detroit", - 729 : "America/Edmonton", - 730 : "America/Eirunepe", - 731 : "America/El_Salvador", - 732 : "America/Fort_Nelson", - 733 : "America/Fortaleza", - 734 : "America/Glace_Bay", - 735 : "America/Goose_Bay", - 736 : "America/Grand_Turk", - 737 : "America/Guatemala", - 738 : "America/Guayaquil", - 739 : "America/Guyana", - 740 : "America/Halifax", - 741 : "America/Havana", - 742 : "America/Hermosillo", - 743 : "America/Indiana/Indianapolis", - 744 : "America/Indiana/Knox", - 745 : "America/Indiana/Marengo", - 746 : "America/Indiana/Petersburg", - 747 : "America/Indiana/Tell_City", - 748 : "America/Indiana/Vevay", - 749 : "America/Indiana/Vincennes", - 750 : "America/Indiana/Winamac", - 751 : "America/Inuvik", - 752 : "America/Iqaluit", - 753 : "America/Jamaica", - 754 : "America/Juneau", - 755 : "America/Kentucky/Louisville", - 756 : "America/Kentucky/Monticello", - 757 : "America/La_Paz", - 758 : "America/Lima", - 759 : "America/Los_Angeles", - 760 : "America/Maceio", - 761 : "America/Managua", - 762 : "America/Manaus", - 763 : "America/Martinique", - 764 : "America/Matamoros", - 765 : "America/Mazatlan", - 766 : "America/Menominee", - 767 : "America/Merida", - 768 : "America/Metlakatla", - 769 : "America/Mexico_City", - 770 : "America/Miquelon", - 771 : "America/Moncton", - 772 : "America/Monterrey", - 773 : "America/Montevideo", - 774 : "America/New_York", - 775 : "America/Nipigon", - 776 : "America/Nome", - 777 : "America/Noronha", - 778 : "America/North_Dakota/Beulah", - 779 : "America/North_Dakota/Center", - 780 : "America/North_Dakota/New_Salem", - 781 : "America/Nuuk", - 782 : "America/Ojinaga", - 783 : "America/Panama", - 784 : "America/Pangnirtung", - 785 : "America/Paramaribo", - 786 : "America/Phoenix", - 787 : "America/Port-au-Prince", - 788 : "America/Porto_Velho", - 789 : "America/Puerto_Rico", - 790 : "America/Punta_Arenas", - 791 : "America/Rainy_River", - 792 : "America/Rankin_Inlet", - 793 : "America/Recife", - 794 : "America/Regina", - 795 : "America/Resolute", - 796 : "America/Rio_Branco", - 797 : "America/Santarem", - 798 : "America/Santiago", - 799 : "America/Santo_Domingo", - 800 : "America/Sao_Paulo", - 801 : "America/Scoresbysund", - 802 : "America/Sitka", - 803 : "America/St_Johns", - 804 : "America/Swift_Current", - 805 : "America/Tegucigalpa", - 806 : "America/Thule", - 807 : "America/Thunder_Bay", - 808 : "America/Tijuana", - 809 : "America/Toronto", - 810 : "America/Vancouver", - 811 : "America/Whitehorse", - 812 : "America/Winnipeg", - 813 : "America/Yakutat", - 814 : "America/Yellowknife", - 815 : "Antarctica/Casey", - 816 : "Antarctica/Davis", - 817 : "Antarctica/Macquarie", - 818 : "Antarctica/Mawson", - 819 : "Antarctica/Palmer", - 820 : "Antarctica/Rothera", - 821 : "Antarctica/Troll", - 822 : "Antarctica/Vostok", - 823 : "Asia/Almaty", - 824 : "Asia/Amman", - 825 : "Asia/Anadyr", - 826 : "Asia/Aqtau", - 827 : "Asia/Aqtobe", - 828 : "Asia/Ashgabat", - 829 : "Asia/Atyrau", - 830 : "Asia/Baghdad", - 831 : "Asia/Baku", - 832 : "Asia/Bangkok", - 833 : "Asia/Barnaul", - 834 : "Asia/Beirut", - 835 : "Asia/Bishkek", - 836 : "Asia/Brunei", - 837 : "Asia/Chita", - 838 : "Asia/Choibalsan", - 839 : "Asia/Colombo", - 840 : "Asia/Damascus", - 841 : "Asia/Dhaka", - 842 : "Asia/Dili", - 843 : "Asia/Dubai", - 844 : "Asia/Dushanbe", - 845 : "Asia/Famagusta", - 846 : "Asia/Gaza", - 847 : "Asia/Hebron", - 848 : "Asia/Ho_Chi_Minh", - 849 : "Asia/Hong_Kong", - 850 : "Asia/Hovd", - 851 : "Asia/Irkutsk", - 852 : "Asia/Jakarta", - 853 : "Asia/Jayapura", - 854 : "Asia/Jerusalem", - 855 : "Asia/Kabul", - 856 : "Asia/Kamchatka", - 857 : "Asia/Karachi", - 858 : "Asia/Kathmandu", - 859 : "Asia/Khandyga", - 860 : "Asia/Kolkata", - 861 : "Asia/Krasnoyarsk", - 862 : "Asia/Kuala_Lumpur", - 863 : "Asia/Kuching", - 864 : "Asia/Macau", - 865 : "Asia/Magadan", - 866 : "Asia/Makassar", - 867 : "Asia/Manila", - 868 : "Asia/Nicosia", - 869 : "Asia/Novokuznetsk", - 870 : "Asia/Novosibirsk", - 871 : "Asia/Omsk", - 872 : "Asia/Oral", - 873 : "Asia/Pontianak", - 874 : "Asia/Pyongyang", - 875 : "Asia/Qatar", - 876 : "Asia/Qostanay", - 877 : "Asia/Qyzylorda", - 878 : "Asia/Riyadh", - 879 : "Asia/Sakhalin", - 880 : "Asia/Samarkand", - 881 : "Asia/Seoul", - 882 : "Asia/Shanghai", - 883 : "Asia/Singapore", - 884 : "Asia/Srednekolymsk", - 885 : "Asia/Taipei", - 886 : "Asia/Tashkent", - 887 : "Asia/Tbilisi", - 888 : "Asia/Tehran", - 889 : "Asia/Thimphu", - 890 : "Asia/Tokyo", - 891 : "Asia/Tomsk", - 892 : "Asia/Ulaanbaatar", - 893 : "Asia/Urumqi", - 894 : "Asia/Ust-Nera", - 895 : "Asia/Vladivostok", - 896 : "Asia/Yakutsk", - 897 : "Asia/Yangon", - 898 : "Asia/Yekaterinburg", - 899 : "Asia/Yerevan", - 900 : "Atlantic/Azores", - 901 : "Atlantic/Bermuda", - 902 : "Atlantic/Canary", - 903 : "Atlantic/Cape_Verde", - 904 : "Atlantic/Faroe", - 905 : "Atlantic/Madeira", - 906 : "Atlantic/Reykjavik", - 907 : "Atlantic/South_Georgia", - 908 : "Atlantic/Stanley", - 909 : "Australia/Adelaide", - 910 : "Australia/Brisbane", - 911 : "Australia/Broken_Hill", - 912 : "Australia/Darwin", - 913 : "Australia/Eucla", - 914 : "Australia/Hobart", - 915 : "Australia/Lindeman", - 916 : "Australia/Lord_Howe", - 917 : "Australia/Melbourne", - 918 : "Australia/Perth", - 919 : "Australia/Sydney", - 920 : "Etc/GMT", - 921 : "Etc/UTC", - 922 : "Europe/Amsterdam", - 923 : "Europe/Andorra", - 924 : "Europe/Astrakhan", - 925 : "Europe/Athens", - 926 : "Europe/Belgrade", - 927 : "Europe/Berlin", - 928 : "Europe/Brussels", - 929 : "Europe/Bucharest", - 930 : "Europe/Budapest", - 931 : "Europe/Chisinau", - 932 : "Europe/Copenhagen", - 933 : "Europe/Dublin", - 934 : "Europe/Gibraltar", - 935 : "Europe/Helsinki", - 936 : "Europe/Istanbul", - 937 : "Europe/Kaliningrad", - 938 : "Europe/Kiev", - 939 : "Europe/Kirov", - 940 : "Europe/Lisbon", - 941 : "Europe/London", - 942 : "Europe/Luxembourg", - 943 : "Europe/Madrid", - 944 : "Europe/Malta", - 945 : "Europe/Minsk", - 946 : "Europe/Monaco", - 947 : "Europe/Moscow", - 948 : "Europe/Oslo", - 949 : "Europe/Paris", - 950 : "Europe/Prague", - 951 : "Europe/Riga", - 952 : "Europe/Rome", - 953 : "Europe/Samara", - 954 : "Europe/Saratov", - 955 : "Europe/Simferopol", - 956 : "Europe/Sofia", - 957 : "Europe/Stockholm", - 958 : "Europe/Tallinn", - 959 : "Europe/Tirane", - 960 : "Europe/Ulyanovsk", - 961 : "Europe/Uzhgorod", - 962 : "Europe/Vienna", - 963 : "Europe/Vilnius", - 964 : "Europe/Volgograd", - 965 : "Europe/Warsaw", - 966 : "Europe/Zaporozhye", - 967 : "Europe/Zurich", - 968 : "Indian/Chagos", - 969 : "Indian/Christmas", - 970 : "Indian/Cocos", - 971 : "Indian/Kerguelen", - 972 : "Indian/Mahe", - 973 : "Indian/Maldives", - 974 : "Indian/Mauritius", - 975 : "Indian/Reunion", - 976 : "Pacific/Apia", - 977 : "Pacific/Auckland", - 978 : "Pacific/Bougainville", - 979 : "Pacific/Chatham", - 980 : "Pacific/Chuuk", - 981 : "Pacific/Easter", - 982 : "Pacific/Efate", - 983 : "Pacific/Fakaofo", - 984 : "Pacific/Fiji", - 985 : "Pacific/Funafuti", - 986 : "Pacific/Galapagos", - 987 : "Pacific/Gambier", - 988 : "Pacific/Guadalcanal", - 989 : "Pacific/Guam", - 990 : "Pacific/Honolulu", - 991 : "Pacific/Kanton", - 992 : "Pacific/Kiritimati", - 993 : "Pacific/Kosrae", - 994 : "Pacific/Kwajalein", - 995 : "Pacific/Majuro", - 996 : "Pacific/Marquesas", - 997 : "Pacific/Nauru", - 998 : "Pacific/Niue", - 999 : "Pacific/Norfolk", - 1000 : "Pacific/Noumea", - 1001 : "Pacific/Pago_Pago", - 1002 : "Pacific/Palau", - 1003 : "Pacific/Pitcairn", - 1004 : "Pacific/Pohnpei", - 1005 : "Pacific/Port_Moresby", - 1006 : "Pacific/Rarotonga", - 1007 : "Pacific/Tahiti", - 1008 : "Pacific/Tarawa", - 1009 : "Pacific/Tongatapu", - 1010 : "Pacific/Wake", - 1011 : "Pacific/Wallis", + 1: "A", + 2: "B", + 3: "C", + 4: "D", + 5: "E", + 6: "F", + 7: "G", + 8: "H", + 9: "I", + 10: "K", + 11: "L", + 12: "M", + 13: "N", + 14: "O", + 15: "P", + 16: "Q", + 17: "R", + 18: "S", + 19: "T", + 20: "U", + 21: "V", + 22: "W", + 23: "X", + 24: "Y", + 25: "Z", + 32: "AT", + 40: "BT", + 48: "CT", + 56: "ET", + 64: "GT", + 72: "IT", + 80: "KT", + 88: "MT", + 96: "PT", + 104: "ST", + 112: "UT", + 120: "WT", + 128: "ACT", + 129: "ADT", + 130: "AET", + 131: "AFT", + 132: "AMT", + 133: "AoE", + 134: "ART", + 135: "AST", + 136: "AZT", + 144: "BDT", + 145: "BNT", + 146: "BOT", + 147: "BRT", + 148: "BST", + 149: "BTT", + 152: "CAT", + 153: "CCT", + 154: "CDT", + 155: "CET", + 156: "CIT", + 157: "CKT", + 158: "CLT", + 159: "COT", + 160: "CST", + 161: "CVT", + 162: "CXT", + 168: "EAT", + 169: "ECT", + 170: "EDT", + 171: "EET", + 172: "EGT", + 173: "EST", + 176: "FET", + 177: "FJT", + 178: "FKT", + 179: "FNT", + 184: "GET", + 185: "GFT", + 186: "GMT", + 187: "GST", + 188: "GYT", + 192: "HAA", + 193: "HAC", + 194: "HAE", + 195: "HAP", + 196: "HAR", + 197: "HAT", + 198: "HDT", + 199: "HKT", + 200: "HLV", + 201: "HNA", + 202: "HNC", + 203: "HNE", + 204: "HNP", + 205: "HNR", + 206: "HNT", + 207: "HST", + 208: "ICT", + 209: "IDT", + 210: "IOT", + 211: "IST", + 216: "JST", + 224: "KGT", + 225: "KIT", + 226: "KST", + 232: "MCK", + 233: "MDT", + 234: "MEZ", + 235: "MHT", + 236: "MMT", + 237: "MSD", + 238: "MSK", + 239: "MST", + 240: "MUT", + 241: "MVT", + 242: "MYT", + 248: "NCT", + 249: "NDT", + 250: "NFT", + 251: "NPT", + 252: "NRT", + 253: "NST", + 254: "NUT", + 256: "OEZ", + 264: "PDT", + 265: "PET", + 266: "PGT", + 267: "PHT", + 268: "PKT", + 269: "PST", + 270: "PWT", + 271: "PYT", + 272: "RET", + 280: "SBT", + 281: "SCT", + 282: "SGT", + 283: "SRT", + 284: "SST", + 288: "TFT", + 289: "TJT", + 290: "TKT", + 291: "TLT", + 292: "TMT", + 293: "TOT", + 294: "TRT", + 295: "TVT", + 296: "UTC", + 297: "UYT", + 298: "UZT", + 304: "VET", + 305: "VUT", + 312: "WAT", + 313: "WDT", + 314: "WET", + 315: "WEZ", + 316: "WFT", + 317: "WGT", + 318: "WIB", + 319: "WIT", + 320: "WST", + 328: "ACDT", + 329: "ACST", + 330: "ADST", + 331: "AEDT", + 332: "AEST", + 333: "AKDT", + 334: "AKST", + 335: "ALMT", + 336: "AMDT", + 337: "AMST", + 338: "ANAT", + 339: "AQTT", + 340: "AWDT", + 341: "AWST", + 342: "AZOT", + 343: "AZST", + 344: "BDST", + 345: "BRST", + 352: "CAST", + 353: "CDST", + 354: "CEDT", + 355: "CEST", + 356: "CHOT", + 357: "ChST", + 358: "CHUT", + 359: "CIST", + 360: "CLDT", + 361: "CLST", + 368: "DAVT", + 369: "DDUT", + 376: "EADT", + 377: "EAST", + 378: "ECST", + 379: "EDST", + 380: "EEDT", + 381: "EEST", + 382: "EGST", + 384: "FJDT", + 385: "FJST", + 386: "FKDT", + 387: "FKST", + 392: "GALT", + 393: "GAMT", + 394: "GILT", + 400: "HADT", + 401: "HAST", + 402: "HOVT", + 408: "IRDT", + 409: "IRKT", + 410: "IRST", + 416: "KOST", + 417: "KRAT", + 418: "KUYT", + 424: "LHDT", + 425: "LHST", + 426: "LINT", + 432: "MAGT", + 433: "MART", + 434: "MAWT", + 435: "MDST", + 436: "MESZ", + 440: "NFDT", + 441: "NOVT", + 442: "NZDT", + 443: "NZST", + 448: "OESZ", + 449: "OMST", + 450: "ORAT", + 456: "PDST", + 457: "PETT", + 458: "PHOT", + 459: "PMDT", + 460: "PMST", + 461: "PONT", + 462: "PYST", + 464: "QYZT", + 472: "ROTT", + 480: "SAKT", + 481: "SAMT", + 482: "SAST", + 483: "SRET", + 484: "SYOT", + 488: "TAHT", + 489: "TOST", + 496: "ULAT", + 497: "UYST", + 504: "VLAT", + 505: "VOST", + 512: "WAKT", + 513: "WAST", + 514: "WEDT", + 515: "WEST", + 516: "WESZ", + 517: "WGST", + 518: "WITA", + 520: "YAKT", + 521: "YAPT", + 522: "YEKT", + 528: "ACWST", + 529: "ANAST", + 530: "AZODT", + 531: "AZOST", + 536: "CHADT", + 537: "CHAST", + 538: "CHODT", + 539: "CHOST", + 540: "CIDST", + 544: "EASST", + 545: "EFATE", + 552: "HOVDT", + 553: "HOVST", + 560: "IRKST", + 568: "KRAST", + 576: "MAGST", + 584: "NACDT", + 585: "NACST", + 586: "NAEDT", + 587: "NAEST", + 588: "NAMDT", + 589: "NAMST", + 590: "NAPDT", + 591: "NAPST", + 592: "NOVST", + 600: "OMSST", + 608: "PETST", + 616: "SAMST", + 624: "ULAST", + 632: "VLAST", + 640: "WARST", + 648: "YAKST", + 649: "YEKST", + 656: "CHODST", + 664: "HOVDST", + 672: "Africa/Abidjan", + 673: "Africa/Algiers", + 674: "Africa/Bissau", + 675: "Africa/Cairo", + 676: "Africa/Casablanca", + 677: "Africa/Ceuta", + 678: "Africa/El_Aaiun", + 679: "Africa/Johannesburg", + 680: "Africa/Juba", + 681: "Africa/Khartoum", + 682: "Africa/Lagos", + 683: "Africa/Maputo", + 684: "Africa/Monrovia", + 685: "Africa/Nairobi", + 686: "Africa/Ndjamena", + 687: "Africa/Sao_Tome", + 688: "Africa/Tripoli", + 689: "Africa/Tunis", + 690: "Africa/Windhoek", + 691: "America/Adak", + 692: "America/Anchorage", + 693: "America/Araguaina", + 694: "America/Argentina/Buenos_Aires", + 695: "America/Argentina/Catamarca", + 696: "America/Argentina/Cordoba", + 697: "America/Argentina/Jujuy", + 698: "America/Argentina/La_Rioja", + 699: "America/Argentina/Mendoza", + 700: "America/Argentina/Rio_Gallegos", + 701: "America/Argentina/Salta", + 702: "America/Argentina/San_Juan", + 703: "America/Argentina/San_Luis", + 704: "America/Argentina/Tucuman", + 705: "America/Argentina/Ushuaia", + 706: "America/Asuncion", + 707: "America/Bahia", + 708: "America/Bahia_Banderas", + 709: "America/Barbados", + 710: "America/Belem", + 711: "America/Belize", + 712: "America/Boa_Vista", + 713: "America/Bogota", + 714: "America/Boise", + 715: "America/Cambridge_Bay", + 716: "America/Campo_Grande", + 717: "America/Cancun", + 718: "America/Caracas", + 719: "America/Cayenne", + 720: "America/Chicago", + 721: "America/Chihuahua", + 722: "America/Costa_Rica", + 723: "America/Cuiaba", + 724: "America/Danmarkshavn", + 725: "America/Dawson", + 726: "America/Dawson_Creek", + 727: "America/Denver", + 728: "America/Detroit", + 729: "America/Edmonton", + 730: "America/Eirunepe", + 731: "America/El_Salvador", + 732: "America/Fort_Nelson", + 733: "America/Fortaleza", + 734: "America/Glace_Bay", + 735: "America/Goose_Bay", + 736: "America/Grand_Turk", + 737: "America/Guatemala", + 738: "America/Guayaquil", + 739: "America/Guyana", + 740: "America/Halifax", + 741: "America/Havana", + 742: "America/Hermosillo", + 743: "America/Indiana/Indianapolis", + 744: "America/Indiana/Knox", + 745: "America/Indiana/Marengo", + 746: "America/Indiana/Petersburg", + 747: "America/Indiana/Tell_City", + 748: "America/Indiana/Vevay", + 749: "America/Indiana/Vincennes", + 750: "America/Indiana/Winamac", + 751: "America/Inuvik", + 752: "America/Iqaluit", + 753: "America/Jamaica", + 754: "America/Juneau", + 755: "America/Kentucky/Louisville", + 756: "America/Kentucky/Monticello", + 757: "America/La_Paz", + 758: "America/Lima", + 759: "America/Los_Angeles", + 760: "America/Maceio", + 761: "America/Managua", + 762: "America/Manaus", + 763: "America/Martinique", + 764: "America/Matamoros", + 765: "America/Mazatlan", + 766: "America/Menominee", + 767: "America/Merida", + 768: "America/Metlakatla", + 769: "America/Mexico_City", + 770: "America/Miquelon", + 771: "America/Moncton", + 772: "America/Monterrey", + 773: "America/Montevideo", + 774: "America/New_York", + 775: "America/Nipigon", + 776: "America/Nome", + 777: "America/Noronha", + 778: "America/North_Dakota/Beulah", + 779: "America/North_Dakota/Center", + 780: "America/North_Dakota/New_Salem", + 781: "America/Nuuk", + 782: "America/Ojinaga", + 783: "America/Panama", + 784: "America/Pangnirtung", + 785: "America/Paramaribo", + 786: "America/Phoenix", + 787: "America/Port-au-Prince", + 788: "America/Porto_Velho", + 789: "America/Puerto_Rico", + 790: "America/Punta_Arenas", + 791: "America/Rainy_River", + 792: "America/Rankin_Inlet", + 793: "America/Recife", + 794: "America/Regina", + 795: "America/Resolute", + 796: "America/Rio_Branco", + 797: "America/Santarem", + 798: "America/Santiago", + 799: "America/Santo_Domingo", + 800: "America/Sao_Paulo", + 801: "America/Scoresbysund", + 802: "America/Sitka", + 803: "America/St_Johns", + 804: "America/Swift_Current", + 805: "America/Tegucigalpa", + 806: "America/Thule", + 807: "America/Thunder_Bay", + 808: "America/Tijuana", + 809: "America/Toronto", + 810: "America/Vancouver", + 811: "America/Whitehorse", + 812: "America/Winnipeg", + 813: "America/Yakutat", + 814: "America/Yellowknife", + 815: "Antarctica/Casey", + 816: "Antarctica/Davis", + 817: "Antarctica/Macquarie", + 818: "Antarctica/Mawson", + 819: "Antarctica/Palmer", + 820: "Antarctica/Rothera", + 821: "Antarctica/Troll", + 822: "Antarctica/Vostok", + 823: "Asia/Almaty", + 824: "Asia/Amman", + 825: "Asia/Anadyr", + 826: "Asia/Aqtau", + 827: "Asia/Aqtobe", + 828: "Asia/Ashgabat", + 829: "Asia/Atyrau", + 830: "Asia/Baghdad", + 831: "Asia/Baku", + 832: "Asia/Bangkok", + 833: "Asia/Barnaul", + 834: "Asia/Beirut", + 835: "Asia/Bishkek", + 836: "Asia/Brunei", + 837: "Asia/Chita", + 838: "Asia/Choibalsan", + 839: "Asia/Colombo", + 840: "Asia/Damascus", + 841: "Asia/Dhaka", + 842: "Asia/Dili", + 843: "Asia/Dubai", + 844: "Asia/Dushanbe", + 845: "Asia/Famagusta", + 846: "Asia/Gaza", + 847: "Asia/Hebron", + 848: "Asia/Ho_Chi_Minh", + 849: "Asia/Hong_Kong", + 850: "Asia/Hovd", + 851: "Asia/Irkutsk", + 852: "Asia/Jakarta", + 853: "Asia/Jayapura", + 854: "Asia/Jerusalem", + 855: "Asia/Kabul", + 856: "Asia/Kamchatka", + 857: "Asia/Karachi", + 858: "Asia/Kathmandu", + 859: "Asia/Khandyga", + 860: "Asia/Kolkata", + 861: "Asia/Krasnoyarsk", + 862: "Asia/Kuala_Lumpur", + 863: "Asia/Kuching", + 864: "Asia/Macau", + 865: "Asia/Magadan", + 866: "Asia/Makassar", + 867: "Asia/Manila", + 868: "Asia/Nicosia", + 869: "Asia/Novokuznetsk", + 870: "Asia/Novosibirsk", + 871: "Asia/Omsk", + 872: "Asia/Oral", + 873: "Asia/Pontianak", + 874: "Asia/Pyongyang", + 875: "Asia/Qatar", + 876: "Asia/Qostanay", + 877: "Asia/Qyzylorda", + 878: "Asia/Riyadh", + 879: "Asia/Sakhalin", + 880: "Asia/Samarkand", + 881: "Asia/Seoul", + 882: "Asia/Shanghai", + 883: "Asia/Singapore", + 884: "Asia/Srednekolymsk", + 885: "Asia/Taipei", + 886: "Asia/Tashkent", + 887: "Asia/Tbilisi", + 888: "Asia/Tehran", + 889: "Asia/Thimphu", + 890: "Asia/Tokyo", + 891: "Asia/Tomsk", + 892: "Asia/Ulaanbaatar", + 893: "Asia/Urumqi", + 894: "Asia/Ust-Nera", + 895: "Asia/Vladivostok", + 896: "Asia/Yakutsk", + 897: "Asia/Yangon", + 898: "Asia/Yekaterinburg", + 899: "Asia/Yerevan", + 900: "Atlantic/Azores", + 901: "Atlantic/Bermuda", + 902: "Atlantic/Canary", + 903: "Atlantic/Cape_Verde", + 904: "Atlantic/Faroe", + 905: "Atlantic/Madeira", + 906: "Atlantic/Reykjavik", + 907: "Atlantic/South_Georgia", + 908: "Atlantic/Stanley", + 909: "Australia/Adelaide", + 910: "Australia/Brisbane", + 911: "Australia/Broken_Hill", + 912: "Australia/Darwin", + 913: "Australia/Eucla", + 914: "Australia/Hobart", + 915: "Australia/Lindeman", + 916: "Australia/Lord_Howe", + 917: "Australia/Melbourne", + 918: "Australia/Perth", + 919: "Australia/Sydney", + 920: "Etc/GMT", + 921: "Etc/UTC", + 922: "Europe/Amsterdam", + 923: "Europe/Andorra", + 924: "Europe/Astrakhan", + 925: "Europe/Athens", + 926: "Europe/Belgrade", + 927: "Europe/Berlin", + 928: "Europe/Brussels", + 929: "Europe/Bucharest", + 930: "Europe/Budapest", + 931: "Europe/Chisinau", + 932: "Europe/Copenhagen", + 933: "Europe/Dublin", + 934: "Europe/Gibraltar", + 935: "Europe/Helsinki", + 936: "Europe/Istanbul", + 937: "Europe/Kaliningrad", + 938: "Europe/Kiev", + 939: "Europe/Kirov", + 940: "Europe/Lisbon", + 941: "Europe/London", + 942: "Europe/Luxembourg", + 943: "Europe/Madrid", + 944: "Europe/Malta", + 945: "Europe/Minsk", + 946: "Europe/Monaco", + 947: "Europe/Moscow", + 948: "Europe/Oslo", + 949: "Europe/Paris", + 950: "Europe/Prague", + 951: "Europe/Riga", + 952: "Europe/Rome", + 953: "Europe/Samara", + 954: "Europe/Saratov", + 955: "Europe/Simferopol", + 956: "Europe/Sofia", + 957: "Europe/Stockholm", + 958: "Europe/Tallinn", + 959: "Europe/Tirane", + 960: "Europe/Ulyanovsk", + 961: "Europe/Uzhgorod", + 962: "Europe/Vienna", + 963: "Europe/Vilnius", + 964: "Europe/Volgograd", + 965: "Europe/Warsaw", + 966: "Europe/Zaporozhye", + 967: "Europe/Zurich", + 968: "Indian/Chagos", + 969: "Indian/Christmas", + 970: "Indian/Cocos", + 971: "Indian/Kerguelen", + 972: "Indian/Mahe", + 973: "Indian/Maldives", + 974: "Indian/Mauritius", + 975: "Indian/Reunion", + 976: "Pacific/Apia", + 977: "Pacific/Auckland", + 978: "Pacific/Bougainville", + 979: "Pacific/Chatham", + 980: "Pacific/Chuuk", + 981: "Pacific/Easter", + 982: "Pacific/Efate", + 983: "Pacific/Fakaofo", + 984: "Pacific/Fiji", + 985: "Pacific/Funafuti", + 986: "Pacific/Galapagos", + 987: "Pacific/Gambier", + 988: "Pacific/Guadalcanal", + 989: "Pacific/Guam", + 990: "Pacific/Honolulu", + 991: "Pacific/Kanton", + 992: "Pacific/Kiritimati", + 993: "Pacific/Kosrae", + 994: "Pacific/Kwajalein", + 995: "Pacific/Majuro", + 996: "Pacific/Marquesas", + 997: "Pacific/Nauru", + 998: "Pacific/Niue", + 999: "Pacific/Norfolk", + 1000: "Pacific/Noumea", + 1001: "Pacific/Pago_Pago", + 1002: "Pacific/Palau", + 1003: "Pacific/Pitcairn", + 1004: "Pacific/Pohnpei", + 1005: "Pacific/Port_Moresby", + 1006: "Pacific/Rarotonga", + 1007: "Pacific/Tahiti", + 1008: "Pacific/Tarawa", + 1009: "Pacific/Tongatapu", + 1010: "Pacific/Wake", + 1011: "Pacific/Wallis", } timezoneToIndex = { - "A" : 1, - "B" : 2, - "C" : 3, - "D" : 4, - "E" : 5, - "F" : 6, - "G" : 7, - "H" : 8, - "I" : 9, - "K" : 10, - "L" : 11, - "M" : 12, - "N" : 13, - "O" : 14, - "P" : 15, - "Q" : 16, - "R" : 17, - "S" : 18, - "T" : 19, - "U" : 20, - "V" : 21, - "W" : 22, - "X" : 23, - "Y" : 24, - "Z" : 25, - "AT" : 32, - "BT" : 40, - "CT" : 48, - "ET" : 56, - "GT" : 64, - "IT" : 72, - "KT" : 80, - "MT" : 88, - "PT" : 96, - "ST" : 104, - "UT" : 112, - "WT" : 120, - "ACT" : 128, - "ADT" : 129, - "AET" : 130, - "AFT" : 131, - "AMT" : 132, - "AoE" : 133, - "ART" : 134, - "AST" : 135, - "AZT" : 136, - "BDT" : 144, - "BNT" : 145, - "BOT" : 146, - "BRT" : 147, - "BST" : 148, - "BTT" : 149, - "CAT" : 152, - "CCT" : 153, - "CDT" : 154, - "CET" : 155, - "CIT" : 156, - "CKT" : 157, - "CLT" : 158, - "COT" : 159, - "CST" : 160, - "CVT" : 161, - "CXT" : 162, - "EAT" : 168, - "ECT" : 169, - "EDT" : 170, - "EET" : 171, - "EGT" : 172, - "EST" : 173, - "FET" : 176, - "FJT" : 177, - "FKT" : 178, - "FNT" : 179, - "GET" : 184, - "GFT" : 185, - "GMT" : 186, - "GST" : 187, - "GYT" : 188, - "HAA" : 192, - "HAC" : 193, - "HAE" : 194, - "HAP" : 195, - "HAR" : 196, - "HAT" : 197, - "HDT" : 198, - "HKT" : 199, - "HLV" : 200, - "HNA" : 201, - "HNC" : 202, - "HNE" : 203, - "HNP" : 204, - "HNR" : 205, - "HNT" : 206, - "HST" : 207, - "ICT" : 208, - "IDT" : 209, - "IOT" : 210, - "IST" : 211, - "JST" : 216, - "KGT" : 224, - "KIT" : 225, - "KST" : 226, - "MCK" : 232, - "MDT" : 233, - "MEZ" : 234, - "MHT" : 235, - "MMT" : 236, - "MSD" : 237, - "MSK" : 238, - "MST" : 239, - "MUT" : 240, - "MVT" : 241, - "MYT" : 242, - "NCT" : 248, - "NDT" : 249, - "NFT" : 250, - "NPT" : 251, - "NRT" : 252, - "NST" : 253, - "NUT" : 254, - "OEZ" : 256, - "PDT" : 264, - "PET" : 265, - "PGT" : 266, - "PHT" : 267, - "PKT" : 268, - "PST" : 269, - "PWT" : 270, - "PYT" : 271, - "RET" : 272, - "SBT" : 280, - "SCT" : 281, - "SGT" : 282, - "SRT" : 283, - "SST" : 284, - "TFT" : 288, - "TJT" : 289, - "TKT" : 290, - "TLT" : 291, - "TMT" : 292, - "TOT" : 293, - "TRT" : 294, - "TVT" : 295, - "UTC" : 296, - "UYT" : 297, - "UZT" : 298, - "VET" : 304, - "VUT" : 305, - "WAT" : 312, - "WDT" : 313, - "WET" : 314, - "WEZ" : 315, - "WFT" : 316, - "WGT" : 317, - "WIB" : 318, - "WIT" : 319, - "WST" : 320, - "ACDT" : 328, - "ACST" : 329, - "ADST" : 330, - "AEDT" : 331, - "AEST" : 332, - "AKDT" : 333, - "AKST" : 334, - "ALMT" : 335, - "AMDT" : 336, - "AMST" : 337, - "ANAT" : 338, - "AQTT" : 339, - "AWDT" : 340, - "AWST" : 341, - "AZOT" : 342, - "AZST" : 343, - "BDST" : 344, - "BRST" : 345, - "CAST" : 352, - "CDST" : 353, - "CEDT" : 354, - "CEST" : 355, - "CHOT" : 356, - "ChST" : 357, - "CHUT" : 358, - "CIST" : 359, - "CLDT" : 360, - "CLST" : 361, - "DAVT" : 368, - "DDUT" : 369, - "EADT" : 376, - "EAST" : 377, - "ECST" : 378, - "EDST" : 379, - "EEDT" : 380, - "EEST" : 381, - "EGST" : 382, - "FJDT" : 384, - "FJST" : 385, - "FKDT" : 386, - "FKST" : 387, - "GALT" : 392, - "GAMT" : 393, - "GILT" : 394, - "HADT" : 400, - "HAST" : 401, - "HOVT" : 402, - "IRDT" : 408, - "IRKT" : 409, - "IRST" : 410, - "KOST" : 416, - "KRAT" : 417, - "KUYT" : 418, - "LHDT" : 424, - "LHST" : 425, - "LINT" : 426, - "MAGT" : 432, - "MART" : 433, - "MAWT" : 434, - "MDST" : 435, - "MESZ" : 436, - "NFDT" : 440, - "NOVT" : 441, - "NZDT" : 442, - "NZST" : 443, - "OESZ" : 448, - "OMST" : 449, - "ORAT" : 450, - "PDST" : 456, - "PETT" : 457, - "PHOT" : 458, - "PMDT" : 459, - "PMST" : 460, - "PONT" : 461, - "PYST" : 462, - "QYZT" : 464, - "ROTT" : 472, - "SAKT" : 480, - "SAMT" : 481, - "SAST" : 482, - "SRET" : 483, - "SYOT" : 484, - "TAHT" : 488, - "TOST" : 489, - "ULAT" : 496, - "UYST" : 497, - "VLAT" : 504, - "VOST" : 505, - "WAKT" : 512, - "WAST" : 513, - "WEDT" : 514, - "WEST" : 515, - "WESZ" : 516, - "WGST" : 517, - "WITA" : 518, - "YAKT" : 520, - "YAPT" : 521, - "YEKT" : 522, - "ACWST" : 528, - "ANAST" : 529, - "AZODT" : 530, - "AZOST" : 531, - "CHADT" : 536, - "CHAST" : 537, - "CHODT" : 538, - "CHOST" : 539, - "CIDST" : 540, - "EASST" : 544, - "EFATE" : 545, - "HOVDT" : 552, - "HOVST" : 553, - "IRKST" : 560, - "KRAST" : 568, - "MAGST" : 576, - "NACDT" : 584, - "NACST" : 585, - "NAEDT" : 586, - "NAEST" : 587, - "NAMDT" : 588, - "NAMST" : 589, - "NAPDT" : 590, - "NAPST" : 591, - "NOVST" : 592, - "OMSST" : 600, - "PETST" : 608, - "SAMST" : 616, - "ULAST" : 624, - "VLAST" : 632, - "WARST" : 640, - "YAKST" : 648, - "YEKST" : 649, - "CHODST" : 656, - "HOVDST" : 664, - "Africa/Abidjan" : 672, - "Africa/Algiers" : 673, - "Africa/Bissau" : 674, - "Africa/Cairo" : 675, - "Africa/Casablanca" : 676, - "Africa/Ceuta" : 677, - "Africa/El_Aaiun" : 678, - "Africa/Johannesburg" : 679, - "Africa/Juba" : 680, - "Africa/Khartoum" : 681, - "Africa/Lagos" : 682, - "Africa/Maputo" : 683, - "Africa/Monrovia" : 684, - "Africa/Nairobi" : 685, - "Africa/Ndjamena" : 686, - "Africa/Sao_Tome" : 687, - "Africa/Tripoli" : 688, - "Africa/Tunis" : 689, - "Africa/Windhoek" : 690, - "America/Adak" : 691, - "America/Anchorage" : 692, - "America/Araguaina" : 693, - "America/Argentina/Buenos_Aires" : 694, - "America/Argentina/Catamarca" : 695, - "America/Argentina/Cordoba" : 696, - "America/Argentina/Jujuy" : 697, - "America/Argentina/La_Rioja" : 698, - "America/Argentina/Mendoza" : 699, - "America/Argentina/Rio_Gallegos" : 700, - "America/Argentina/Salta" : 701, - "America/Argentina/San_Juan" : 702, - "America/Argentina/San_Luis" : 703, - "America/Argentina/Tucuman" : 704, - "America/Argentina/Ushuaia" : 705, - "America/Asuncion" : 706, - "America/Bahia" : 707, - "America/Bahia_Banderas" : 708, - "America/Barbados" : 709, - "America/Belem" : 710, - "America/Belize" : 711, - "America/Boa_Vista" : 712, - "America/Bogota" : 713, - "America/Boise" : 714, - "America/Cambridge_Bay" : 715, - "America/Campo_Grande" : 716, - "America/Cancun" : 717, - "America/Caracas" : 718, - "America/Cayenne" : 719, - "America/Chicago" : 720, - "America/Chihuahua" : 721, - "America/Costa_Rica" : 722, - "America/Cuiaba" : 723, - "America/Danmarkshavn" : 724, - "America/Dawson" : 725, - "America/Dawson_Creek" : 726, - "America/Denver" : 727, - "America/Detroit" : 728, - "America/Edmonton" : 729, - "America/Eirunepe" : 730, - "America/El_Salvador" : 731, - "America/Fort_Nelson" : 732, - "America/Fortaleza" : 733, - "America/Glace_Bay" : 734, - "America/Goose_Bay" : 735, - "America/Grand_Turk" : 736, - "America/Guatemala" : 737, - "America/Guayaquil" : 738, - "America/Guyana" : 739, - "America/Halifax" : 740, - "America/Havana" : 741, - "America/Hermosillo" : 742, - "America/Indiana/Indianapolis" : 743, - "America/Indiana/Knox" : 744, - "America/Indiana/Marengo" : 745, - "America/Indiana/Petersburg" : 746, - "America/Indiana/Tell_City" : 747, - "America/Indiana/Vevay" : 748, - "America/Indiana/Vincennes" : 749, - "America/Indiana/Winamac" : 750, - "America/Inuvik" : 751, - "America/Iqaluit" : 752, - "America/Jamaica" : 753, - "America/Juneau" : 754, - "America/Kentucky/Louisville" : 755, - "America/Kentucky/Monticello" : 756, - "America/La_Paz" : 757, - "America/Lima" : 758, - "America/Los_Angeles" : 759, - "America/Maceio" : 760, - "America/Managua" : 761, - "America/Manaus" : 762, - "America/Martinique" : 763, - "America/Matamoros" : 764, - "America/Mazatlan" : 765, - "America/Menominee" : 766, - "America/Merida" : 767, - "America/Metlakatla" : 768, - "America/Mexico_City" : 769, - "America/Miquelon" : 770, - "America/Moncton" : 771, - "America/Monterrey" : 772, - "America/Montevideo" : 773, - "America/New_York" : 774, - "America/Nipigon" : 775, - "America/Nome" : 776, - "America/Noronha" : 777, - "America/North_Dakota/Beulah" : 778, - "America/North_Dakota/Center" : 779, - "America/North_Dakota/New_Salem" : 780, - "America/Nuuk" : 781, - "America/Ojinaga" : 782, - "America/Panama" : 783, - "America/Pangnirtung" : 784, - "America/Paramaribo" : 785, - "America/Phoenix" : 786, - "America/Port-au-Prince" : 787, - "America/Porto_Velho" : 788, - "America/Puerto_Rico" : 789, - "America/Punta_Arenas" : 790, - "America/Rainy_River" : 791, - "America/Rankin_Inlet" : 792, - "America/Recife" : 793, - "America/Regina" : 794, - "America/Resolute" : 795, - "America/Rio_Branco" : 796, - "America/Santarem" : 797, - "America/Santiago" : 798, - "America/Santo_Domingo" : 799, - "America/Sao_Paulo" : 800, - "America/Scoresbysund" : 801, - "America/Sitka" : 802, - "America/St_Johns" : 803, - "America/Swift_Current" : 804, - "America/Tegucigalpa" : 805, - "America/Thule" : 806, - "America/Thunder_Bay" : 807, - "America/Tijuana" : 808, - "America/Toronto" : 809, - "America/Vancouver" : 810, - "America/Whitehorse" : 811, - "America/Winnipeg" : 812, - "America/Yakutat" : 813, - "America/Yellowknife" : 814, - "Antarctica/Casey" : 815, - "Antarctica/Davis" : 816, - "Antarctica/Macquarie" : 817, - "Antarctica/Mawson" : 818, - "Antarctica/Palmer" : 819, - "Antarctica/Rothera" : 820, - "Antarctica/Troll" : 821, - "Antarctica/Vostok" : 822, - "Asia/Almaty" : 823, - "Asia/Amman" : 824, - "Asia/Anadyr" : 825, - "Asia/Aqtau" : 826, - "Asia/Aqtobe" : 827, - "Asia/Ashgabat" : 828, - "Asia/Atyrau" : 829, - "Asia/Baghdad" : 830, - "Asia/Baku" : 831, - "Asia/Bangkok" : 832, - "Asia/Barnaul" : 833, - "Asia/Beirut" : 834, - "Asia/Bishkek" : 835, - "Asia/Brunei" : 836, - "Asia/Chita" : 837, - "Asia/Choibalsan" : 838, - "Asia/Colombo" : 839, - "Asia/Damascus" : 840, - "Asia/Dhaka" : 841, - "Asia/Dili" : 842, - "Asia/Dubai" : 843, - "Asia/Dushanbe" : 844, - "Asia/Famagusta" : 845, - "Asia/Gaza" : 846, - "Asia/Hebron" : 847, - "Asia/Ho_Chi_Minh" : 848, - "Asia/Hong_Kong" : 849, - "Asia/Hovd" : 850, - "Asia/Irkutsk" : 851, - "Asia/Jakarta" : 852, - "Asia/Jayapura" : 853, - "Asia/Jerusalem" : 854, - "Asia/Kabul" : 855, - "Asia/Kamchatka" : 856, - "Asia/Karachi" : 857, - "Asia/Kathmandu" : 858, - "Asia/Khandyga" : 859, - "Asia/Kolkata" : 860, - "Asia/Krasnoyarsk" : 861, - "Asia/Kuala_Lumpur" : 862, - "Asia/Kuching" : 863, - "Asia/Macau" : 864, - "Asia/Magadan" : 865, - "Asia/Makassar" : 866, - "Asia/Manila" : 867, - "Asia/Nicosia" : 868, - "Asia/Novokuznetsk" : 869, - "Asia/Novosibirsk" : 870, - "Asia/Omsk" : 871, - "Asia/Oral" : 872, - "Asia/Pontianak" : 873, - "Asia/Pyongyang" : 874, - "Asia/Qatar" : 875, - "Asia/Qostanay" : 876, - "Asia/Qyzylorda" : 877, - "Asia/Riyadh" : 878, - "Asia/Sakhalin" : 879, - "Asia/Samarkand" : 880, - "Asia/Seoul" : 881, - "Asia/Shanghai" : 882, - "Asia/Singapore" : 883, - "Asia/Srednekolymsk" : 884, - "Asia/Taipei" : 885, - "Asia/Tashkent" : 886, - "Asia/Tbilisi" : 887, - "Asia/Tehran" : 888, - "Asia/Thimphu" : 889, - "Asia/Tokyo" : 890, - "Asia/Tomsk" : 891, - "Asia/Ulaanbaatar" : 892, - "Asia/Urumqi" : 893, - "Asia/Ust-Nera" : 894, - "Asia/Vladivostok" : 895, - "Asia/Yakutsk" : 896, - "Asia/Yangon" : 897, - "Asia/Yekaterinburg" : 898, - "Asia/Yerevan" : 899, - "Atlantic/Azores" : 900, - "Atlantic/Bermuda" : 901, - "Atlantic/Canary" : 902, - "Atlantic/Cape_Verde" : 903, - "Atlantic/Faroe" : 904, - "Atlantic/Madeira" : 905, - "Atlantic/Reykjavik" : 906, - "Atlantic/South_Georgia" : 907, - "Atlantic/Stanley" : 908, - "Australia/Adelaide" : 909, - "Australia/Brisbane" : 910, - "Australia/Broken_Hill" : 911, - "Australia/Darwin" : 912, - "Australia/Eucla" : 913, - "Australia/Hobart" : 914, - "Australia/Lindeman" : 915, - "Australia/Lord_Howe" : 916, - "Australia/Melbourne" : 917, - "Australia/Perth" : 918, - "Australia/Sydney" : 919, - "Etc/GMT" : 920, - "Etc/UTC" : 921, - "Europe/Amsterdam" : 922, - "Europe/Andorra" : 923, - "Europe/Astrakhan" : 924, - "Europe/Athens" : 925, - "Europe/Belgrade" : 926, - "Europe/Berlin" : 927, - "Europe/Brussels" : 928, - "Europe/Bucharest" : 929, - "Europe/Budapest" : 930, - "Europe/Chisinau" : 931, - "Europe/Copenhagen" : 932, - "Europe/Dublin" : 933, - "Europe/Gibraltar" : 934, - "Europe/Helsinki" : 935, - "Europe/Istanbul" : 936, - "Europe/Kaliningrad" : 937, - "Europe/Kiev" : 938, - "Europe/Kirov" : 939, - "Europe/Lisbon" : 940, - "Europe/London" : 941, - "Europe/Luxembourg" : 942, - "Europe/Madrid" : 943, - "Europe/Malta" : 944, - "Europe/Minsk" : 945, - "Europe/Monaco" : 946, - "Europe/Moscow" : 947, - "Europe/Oslo" : 948, - "Europe/Paris" : 949, - "Europe/Prague" : 950, - "Europe/Riga" : 951, - "Europe/Rome" : 952, - "Europe/Samara" : 953, - "Europe/Saratov" : 954, - "Europe/Simferopol" : 955, - "Europe/Sofia" : 956, - "Europe/Stockholm" : 957, - "Europe/Tallinn" : 958, - "Europe/Tirane" : 959, - "Europe/Ulyanovsk" : 960, - "Europe/Uzhgorod" : 961, - "Europe/Vienna" : 962, - "Europe/Vilnius" : 963, - "Europe/Volgograd" : 964, - "Europe/Warsaw" : 965, - "Europe/Zaporozhye" : 966, - "Europe/Zurich" : 967, - "Indian/Chagos" : 968, - "Indian/Christmas" : 969, - "Indian/Cocos" : 970, - "Indian/Kerguelen" : 971, - "Indian/Mahe" : 972, - "Indian/Maldives" : 973, - "Indian/Mauritius" : 974, - "Indian/Reunion" : 975, - "Pacific/Apia" : 976, - "Pacific/Auckland" : 977, - "Pacific/Bougainville" : 978, - "Pacific/Chatham" : 979, - "Pacific/Chuuk" : 980, - "Pacific/Easter" : 981, - "Pacific/Efate" : 982, - "Pacific/Fakaofo" : 983, - "Pacific/Fiji" : 984, - "Pacific/Funafuti" : 985, - "Pacific/Galapagos" : 986, - "Pacific/Gambier" : 987, - "Pacific/Guadalcanal" : 988, - "Pacific/Guam" : 989, - "Pacific/Honolulu" : 990, - "Pacific/Kanton" : 991, - "Pacific/Kiritimati" : 992, - "Pacific/Kosrae" : 993, - "Pacific/Kwajalein" : 994, - "Pacific/Majuro" : 995, - "Pacific/Marquesas" : 996, - "Pacific/Nauru" : 997, - "Pacific/Niue" : 998, - "Pacific/Norfolk" : 999, - "Pacific/Noumea" : 1000, - "Pacific/Pago_Pago" : 1001, - "Pacific/Palau" : 1002, - "Pacific/Pitcairn" : 1003, - "Pacific/Pohnpei" : 1004, - "Pacific/Port_Moresby" : 1005, - "Pacific/Rarotonga" : 1006, - "Pacific/Tahiti" : 1007, - "Pacific/Tarawa" : 1008, - "Pacific/Tongatapu" : 1009, - "Pacific/Wake" : 1010, - "Pacific/Wallis" : 1011, - "Africa/Accra" : 672, - "Africa/Addis_Ababa" : 685, - "Africa/Asmara" : 685, - "Africa/Asmera" : 685, - "Africa/Bamako" : 672, - "Africa/Bangui" : 682, - "Africa/Banjul" : 672, - "Africa/Blantyre" : 683, - "Africa/Brazzaville" : 682, - "Africa/Bujumbura" : 683, - "Africa/Conakry" : 672, - "Africa/Dakar" : 672, - "Africa/Dar_es_Salaam" : 685, - "Africa/Djibouti" : 685, - "Africa/Douala" : 682, - "Africa/Freetown" : 672, - "Africa/Gaborone" : 683, - "Africa/Harare" : 683, - "Africa/Kampala" : 685, - "Africa/Kigali" : 683, - "Africa/Kinshasa" : 682, - "Africa/Libreville" : 682, - "Africa/Lome" : 672, - "Africa/Luanda" : 682, - "Africa/Lubumbashi" : 683, - "Africa/Lusaka" : 683, - "Africa/Malabo" : 682, - "Africa/Maseru" : 679, - "Africa/Mbabane" : 679, - "Africa/Mogadishu" : 685, - "Africa/Niamey" : 682, - "Africa/Nouakchott" : 672, - "Africa/Ouagadougou" : 672, - "Africa/Porto-Novo" : 682, - "Africa/Timbuktu" : 672, - "America/Anguilla" : 789, - "America/Antigua" : 789, - "America/Argentina/ComodRivadavia" : 695, - "America/Aruba" : 789, - "America/Atikokan" : 783, - "America/Atka" : 691, - "America/Blanc-Sablon" : 789, - "America/Buenos_Aires" : 694, - "America/Catamarca" : 695, - "America/Cayman" : 783, - "America/Coral_Harbour" : 783, - "America/Cordoba" : 696, - "America/Creston" : 786, - "America/Curacao" : 789, - "America/Dominica" : 789, - "America/Ensenada" : 808, - "America/Fort_Wayne" : 743, - "America/Godthab" : 781, - "America/Grenada" : 789, - "America/Guadeloupe" : 789, - "America/Indianapolis" : 743, - "America/Jujuy" : 697, - "America/Knox_IN" : 744, - "America/Kralendijk" : 789, - "America/Louisville" : 755, - "America/Lower_Princes" : 789, - "America/Marigot" : 789, - "America/Mendoza" : 699, - "America/Montreal" : 809, - "America/Montserrat" : 789, - "America/Nassau" : 809, - "America/Port_of_Spain" : 789, - "America/Porto_Acre" : 796, - "America/Rosario" : 696, - "America/Santa_Isabel" : 808, - "America/Shiprock" : 727, - "America/St_Barthelemy" : 789, - "America/St_Kitts" : 789, - "America/St_Lucia" : 789, - "America/St_Thomas" : 789, - "America/St_Vincent" : 789, - "America/Tortola" : 789, - "America/Virgin" : 789, - "Antarctica/DumontDUrville" : 1005, - "Antarctica/McMurdo" : 977, - "Antarctica/South_Pole" : 977, - "Antarctica/Syowa" : 878, - "Arctic/Longyearbyen" : 948, - "Asia/Aden" : 878, - "Asia/Ashkhabad" : 828, - "Asia/Bahrain" : 875, - "Asia/Calcutta" : 860, - "Asia/Chongqing" : 882, - "Asia/Chungking" : 882, - "Asia/Dacca" : 841, - "Asia/Harbin" : 882, - "Asia/Istanbul" : 936, - "Asia/Kashgar" : 893, - "Asia/Katmandu" : 858, - "Asia/Kuwait" : 878, - "Asia/Macao" : 864, - "Asia/Muscat" : 843, - "Asia/Phnom_Penh" : 832, - "Asia/Rangoon" : 897, - "Asia/Saigon" : 848, - "Asia/Tel_Aviv" : 854, - "Asia/Thimbu" : 889, - "Asia/Ujung_Pandang" : 866, - "Asia/Ulan_Bator" : 892, - "Asia/Vientiane" : 832, - "Atlantic/Faeroe" : 904, - "Atlantic/Jan_Mayen" : 948, - "Atlantic/St_Helena" : 672, - "Australia/ACT" : 919, - "Australia/Canberra" : 919, - "Australia/Currie" : 914, - "Australia/LHI" : 916, - "Australia/NSW" : 919, - "Australia/North" : 912, - "Australia/Queensland" : 910, - "Australia/South" : 909, - "Australia/Tasmania" : 914, - "Australia/Victoria" : 917, - "Australia/West" : 918, - "Australia/Yancowinna" : 911, - "Brazil/Acre" : 796, - "Brazil/DeNoronha" : 777, - "Brazil/East" : 800, - "Brazil/West" : 762, - "Canada/Atlantic" : 740, - "Canada/Central" : 812, - "Canada/Eastern" : 809, - "Canada/Mountain" : 729, - "Canada/Newfoundland" : 803, - "Canada/Pacific" : 810, - "Canada/Saskatchewan" : 794, - "Canada/Yukon" : 811, - "Chile/Continental" : 798, - "Chile/EasterIsland" : 981, - "Cuba" : 741, - "Egypt" : 675, - "Eire" : 933, - "Etc/GMT+0" : 920, - "Etc/GMT-0" : 920, - "Etc/GMT0" : 920, - "Etc/Greenwich" : 920, - "Etc/UCT" : 921, - "Etc/Universal" : 921, - "Etc/Zulu" : 921, - "Europe/Belfast" : 941, - "Europe/Bratislava" : 950, - "Europe/Busingen" : 967, - "Europe/Guernsey" : 941, - "Europe/Isle_of_Man" : 941, - "Europe/Jersey" : 941, - "Europe/Ljubljana" : 926, - "Europe/Mariehamn" : 935, - "Europe/Nicosia" : 868, - "Europe/Podgorica" : 926, - "Europe/San_Marino" : 952, - "Europe/Sarajevo" : 926, - "Europe/Skopje" : 926, - "Europe/Tiraspol" : 931, - "Europe/Vaduz" : 967, - "Europe/Vatican" : 952, - "Europe/Zagreb" : 926, - "GB" : 941, - "GB-Eire" : 941, - "GMT+0" : 920, - "GMT-0" : 920, - "GMT0" : 920, - "Greenwich" : 920, - "Hongkong" : 849, - "Iceland" : 906, - "Indian/Antananarivo" : 685, - "Indian/Comoro" : 685, - "Indian/Mayotte" : 685, - "Iran" : 888, - "Israel" : 854, - "Jamaica" : 753, - "Japan" : 890, - "Kwajalein" : 994, - "Libya" : 688, - "Mexico/BajaNorte" : 808, - "Mexico/BajaSur" : 765, - "Mexico/General" : 769, - "NZ" : 977, - "NZ-CHAT" : 979, - "Navajo" : 727, - "PRC" : 882, - "Pacific/Enderbury" : 991, - "Pacific/Johnston" : 990, - "Pacific/Midway" : 1001, - "Pacific/Ponape" : 1004, - "Pacific/Saipan" : 989, - "Pacific/Samoa" : 1001, - "Pacific/Truk" : 980, - "Pacific/Yap" : 980, - "Poland" : 965, - "Portugal" : 940, - "ROC" : 885, - "ROK" : 881, - "Singapore" : 883, - "Turkey" : 936, - "UCT" : 921, - "US/Alaska" : 692, - "US/Aleutian" : 691, - "US/Arizona" : 786, - "US/Central" : 720, - "US/East-Indiana" : 743, - "US/Eastern" : 774, - "US/Hawaii" : 990, - "US/Indiana-Starke" : 744, - "US/Michigan" : 728, - "US/Mountain" : 727, - "US/Pacific" : 759, - "US/Samoa" : 1001, - "Universal" : 921, - "W-SU" : 947, - "Zulu" : 921, + "A": 1, + "B": 2, + "C": 3, + "D": 4, + "E": 5, + "F": 6, + "G": 7, + "H": 8, + "I": 9, + "K": 10, + "L": 11, + "M": 12, + "N": 13, + "O": 14, + "P": 15, + "Q": 16, + "R": 17, + "S": 18, + "T": 19, + "U": 20, + "V": 21, + "W": 22, + "X": 23, + "Y": 24, + "Z": 25, + "AT": 32, + "BT": 40, + "CT": 48, + "ET": 56, + "GT": 64, + "IT": 72, + "KT": 80, + "MT": 88, + "PT": 96, + "ST": 104, + "UT": 112, + "WT": 120, + "ACT": 128, + "ADT": 129, + "AET": 130, + "AFT": 131, + "AMT": 132, + "AoE": 133, + "ART": 134, + "AST": 135, + "AZT": 136, + "BDT": 144, + "BNT": 145, + "BOT": 146, + "BRT": 147, + "BST": 148, + "BTT": 149, + "CAT": 152, + "CCT": 153, + "CDT": 154, + "CET": 155, + "CIT": 156, + "CKT": 157, + "CLT": 158, + "COT": 159, + "CST": 160, + "CVT": 161, + "CXT": 162, + "EAT": 168, + "ECT": 169, + "EDT": 170, + "EET": 171, + "EGT": 172, + "EST": 173, + "FET": 176, + "FJT": 177, + "FKT": 178, + "FNT": 179, + "GET": 184, + "GFT": 185, + "GMT": 186, + "GST": 187, + "GYT": 188, + "HAA": 192, + "HAC": 193, + "HAE": 194, + "HAP": 195, + "HAR": 196, + "HAT": 197, + "HDT": 198, + "HKT": 199, + "HLV": 200, + "HNA": 201, + "HNC": 202, + "HNE": 203, + "HNP": 204, + "HNR": 205, + "HNT": 206, + "HST": 207, + "ICT": 208, + "IDT": 209, + "IOT": 210, + "IST": 211, + "JST": 216, + "KGT": 224, + "KIT": 225, + "KST": 226, + "MCK": 232, + "MDT": 233, + "MEZ": 234, + "MHT": 235, + "MMT": 236, + "MSD": 237, + "MSK": 238, + "MST": 239, + "MUT": 240, + "MVT": 241, + "MYT": 242, + "NCT": 248, + "NDT": 249, + "NFT": 250, + "NPT": 251, + "NRT": 252, + "NST": 253, + "NUT": 254, + "OEZ": 256, + "PDT": 264, + "PET": 265, + "PGT": 266, + "PHT": 267, + "PKT": 268, + "PST": 269, + "PWT": 270, + "PYT": 271, + "RET": 272, + "SBT": 280, + "SCT": 281, + "SGT": 282, + "SRT": 283, + "SST": 284, + "TFT": 288, + "TJT": 289, + "TKT": 290, + "TLT": 291, + "TMT": 292, + "TOT": 293, + "TRT": 294, + "TVT": 295, + "UTC": 296, + "UYT": 297, + "UZT": 298, + "VET": 304, + "VUT": 305, + "WAT": 312, + "WDT": 313, + "WET": 314, + "WEZ": 315, + "WFT": 316, + "WGT": 317, + "WIB": 318, + "WIT": 319, + "WST": 320, + "ACDT": 328, + "ACST": 329, + "ADST": 330, + "AEDT": 331, + "AEST": 332, + "AKDT": 333, + "AKST": 334, + "ALMT": 335, + "AMDT": 336, + "AMST": 337, + "ANAT": 338, + "AQTT": 339, + "AWDT": 340, + "AWST": 341, + "AZOT": 342, + "AZST": 343, + "BDST": 344, + "BRST": 345, + "CAST": 352, + "CDST": 353, + "CEDT": 354, + "CEST": 355, + "CHOT": 356, + "ChST": 357, + "CHUT": 358, + "CIST": 359, + "CLDT": 360, + "CLST": 361, + "DAVT": 368, + "DDUT": 369, + "EADT": 376, + "EAST": 377, + "ECST": 378, + "EDST": 379, + "EEDT": 380, + "EEST": 381, + "EGST": 382, + "FJDT": 384, + "FJST": 385, + "FKDT": 386, + "FKST": 387, + "GALT": 392, + "GAMT": 393, + "GILT": 394, + "HADT": 400, + "HAST": 401, + "HOVT": 402, + "IRDT": 408, + "IRKT": 409, + "IRST": 410, + "KOST": 416, + "KRAT": 417, + "KUYT": 418, + "LHDT": 424, + "LHST": 425, + "LINT": 426, + "MAGT": 432, + "MART": 433, + "MAWT": 434, + "MDST": 435, + "MESZ": 436, + "NFDT": 440, + "NOVT": 441, + "NZDT": 442, + "NZST": 443, + "OESZ": 448, + "OMST": 449, + "ORAT": 450, + "PDST": 456, + "PETT": 457, + "PHOT": 458, + "PMDT": 459, + "PMST": 460, + "PONT": 461, + "PYST": 462, + "QYZT": 464, + "ROTT": 472, + "SAKT": 480, + "SAMT": 481, + "SAST": 482, + "SRET": 483, + "SYOT": 484, + "TAHT": 488, + "TOST": 489, + "ULAT": 496, + "UYST": 497, + "VLAT": 504, + "VOST": 505, + "WAKT": 512, + "WAST": 513, + "WEDT": 514, + "WEST": 515, + "WESZ": 516, + "WGST": 517, + "WITA": 518, + "YAKT": 520, + "YAPT": 521, + "YEKT": 522, + "ACWST": 528, + "ANAST": 529, + "AZODT": 530, + "AZOST": 531, + "CHADT": 536, + "CHAST": 537, + "CHODT": 538, + "CHOST": 539, + "CIDST": 540, + "EASST": 544, + "EFATE": 545, + "HOVDT": 552, + "HOVST": 553, + "IRKST": 560, + "KRAST": 568, + "MAGST": 576, + "NACDT": 584, + "NACST": 585, + "NAEDT": 586, + "NAEST": 587, + "NAMDT": 588, + "NAMST": 589, + "NAPDT": 590, + "NAPST": 591, + "NOVST": 592, + "OMSST": 600, + "PETST": 608, + "SAMST": 616, + "ULAST": 624, + "VLAST": 632, + "WARST": 640, + "YAKST": 648, + "YEKST": 649, + "CHODST": 656, + "HOVDST": 664, + "Africa/Abidjan": 672, + "Africa/Algiers": 673, + "Africa/Bissau": 674, + "Africa/Cairo": 675, + "Africa/Casablanca": 676, + "Africa/Ceuta": 677, + "Africa/El_Aaiun": 678, + "Africa/Johannesburg": 679, + "Africa/Juba": 680, + "Africa/Khartoum": 681, + "Africa/Lagos": 682, + "Africa/Maputo": 683, + "Africa/Monrovia": 684, + "Africa/Nairobi": 685, + "Africa/Ndjamena": 686, + "Africa/Sao_Tome": 687, + "Africa/Tripoli": 688, + "Africa/Tunis": 689, + "Africa/Windhoek": 690, + "America/Adak": 691, + "America/Anchorage": 692, + "America/Araguaina": 693, + "America/Argentina/Buenos_Aires": 694, + "America/Argentina/Catamarca": 695, + "America/Argentina/Cordoba": 696, + "America/Argentina/Jujuy": 697, + "America/Argentina/La_Rioja": 698, + "America/Argentina/Mendoza": 699, + "America/Argentina/Rio_Gallegos": 700, + "America/Argentina/Salta": 701, + "America/Argentina/San_Juan": 702, + "America/Argentina/San_Luis": 703, + "America/Argentina/Tucuman": 704, + "America/Argentina/Ushuaia": 705, + "America/Asuncion": 706, + "America/Bahia": 707, + "America/Bahia_Banderas": 708, + "America/Barbados": 709, + "America/Belem": 710, + "America/Belize": 711, + "America/Boa_Vista": 712, + "America/Bogota": 713, + "America/Boise": 714, + "America/Cambridge_Bay": 715, + "America/Campo_Grande": 716, + "America/Cancun": 717, + "America/Caracas": 718, + "America/Cayenne": 719, + "America/Chicago": 720, + "America/Chihuahua": 721, + "America/Costa_Rica": 722, + "America/Cuiaba": 723, + "America/Danmarkshavn": 724, + "America/Dawson": 725, + "America/Dawson_Creek": 726, + "America/Denver": 727, + "America/Detroit": 728, + "America/Edmonton": 729, + "America/Eirunepe": 730, + "America/El_Salvador": 731, + "America/Fort_Nelson": 732, + "America/Fortaleza": 733, + "America/Glace_Bay": 734, + "America/Goose_Bay": 735, + "America/Grand_Turk": 736, + "America/Guatemala": 737, + "America/Guayaquil": 738, + "America/Guyana": 739, + "America/Halifax": 740, + "America/Havana": 741, + "America/Hermosillo": 742, + "America/Indiana/Indianapolis": 743, + "America/Indiana/Knox": 744, + "America/Indiana/Marengo": 745, + "America/Indiana/Petersburg": 746, + "America/Indiana/Tell_City": 747, + "America/Indiana/Vevay": 748, + "America/Indiana/Vincennes": 749, + "America/Indiana/Winamac": 750, + "America/Inuvik": 751, + "America/Iqaluit": 752, + "America/Jamaica": 753, + "America/Juneau": 754, + "America/Kentucky/Louisville": 755, + "America/Kentucky/Monticello": 756, + "America/La_Paz": 757, + "America/Lima": 758, + "America/Los_Angeles": 759, + "America/Maceio": 760, + "America/Managua": 761, + "America/Manaus": 762, + "America/Martinique": 763, + "America/Matamoros": 764, + "America/Mazatlan": 765, + "America/Menominee": 766, + "America/Merida": 767, + "America/Metlakatla": 768, + "America/Mexico_City": 769, + "America/Miquelon": 770, + "America/Moncton": 771, + "America/Monterrey": 772, + "America/Montevideo": 773, + "America/New_York": 774, + "America/Nipigon": 775, + "America/Nome": 776, + "America/Noronha": 777, + "America/North_Dakota/Beulah": 778, + "America/North_Dakota/Center": 779, + "America/North_Dakota/New_Salem": 780, + "America/Nuuk": 781, + "America/Ojinaga": 782, + "America/Panama": 783, + "America/Pangnirtung": 784, + "America/Paramaribo": 785, + "America/Phoenix": 786, + "America/Port-au-Prince": 787, + "America/Porto_Velho": 788, + "America/Puerto_Rico": 789, + "America/Punta_Arenas": 790, + "America/Rainy_River": 791, + "America/Rankin_Inlet": 792, + "America/Recife": 793, + "America/Regina": 794, + "America/Resolute": 795, + "America/Rio_Branco": 796, + "America/Santarem": 797, + "America/Santiago": 798, + "America/Santo_Domingo": 799, + "America/Sao_Paulo": 800, + "America/Scoresbysund": 801, + "America/Sitka": 802, + "America/St_Johns": 803, + "America/Swift_Current": 804, + "America/Tegucigalpa": 805, + "America/Thule": 806, + "America/Thunder_Bay": 807, + "America/Tijuana": 808, + "America/Toronto": 809, + "America/Vancouver": 810, + "America/Whitehorse": 811, + "America/Winnipeg": 812, + "America/Yakutat": 813, + "America/Yellowknife": 814, + "Antarctica/Casey": 815, + "Antarctica/Davis": 816, + "Antarctica/Macquarie": 817, + "Antarctica/Mawson": 818, + "Antarctica/Palmer": 819, + "Antarctica/Rothera": 820, + "Antarctica/Troll": 821, + "Antarctica/Vostok": 822, + "Asia/Almaty": 823, + "Asia/Amman": 824, + "Asia/Anadyr": 825, + "Asia/Aqtau": 826, + "Asia/Aqtobe": 827, + "Asia/Ashgabat": 828, + "Asia/Atyrau": 829, + "Asia/Baghdad": 830, + "Asia/Baku": 831, + "Asia/Bangkok": 832, + "Asia/Barnaul": 833, + "Asia/Beirut": 834, + "Asia/Bishkek": 835, + "Asia/Brunei": 836, + "Asia/Chita": 837, + "Asia/Choibalsan": 838, + "Asia/Colombo": 839, + "Asia/Damascus": 840, + "Asia/Dhaka": 841, + "Asia/Dili": 842, + "Asia/Dubai": 843, + "Asia/Dushanbe": 844, + "Asia/Famagusta": 845, + "Asia/Gaza": 846, + "Asia/Hebron": 847, + "Asia/Ho_Chi_Minh": 848, + "Asia/Hong_Kong": 849, + "Asia/Hovd": 850, + "Asia/Irkutsk": 851, + "Asia/Jakarta": 852, + "Asia/Jayapura": 853, + "Asia/Jerusalem": 854, + "Asia/Kabul": 855, + "Asia/Kamchatka": 856, + "Asia/Karachi": 857, + "Asia/Kathmandu": 858, + "Asia/Khandyga": 859, + "Asia/Kolkata": 860, + "Asia/Krasnoyarsk": 861, + "Asia/Kuala_Lumpur": 862, + "Asia/Kuching": 863, + "Asia/Macau": 864, + "Asia/Magadan": 865, + "Asia/Makassar": 866, + "Asia/Manila": 867, + "Asia/Nicosia": 868, + "Asia/Novokuznetsk": 869, + "Asia/Novosibirsk": 870, + "Asia/Omsk": 871, + "Asia/Oral": 872, + "Asia/Pontianak": 873, + "Asia/Pyongyang": 874, + "Asia/Qatar": 875, + "Asia/Qostanay": 876, + "Asia/Qyzylorda": 877, + "Asia/Riyadh": 878, + "Asia/Sakhalin": 879, + "Asia/Samarkand": 880, + "Asia/Seoul": 881, + "Asia/Shanghai": 882, + "Asia/Singapore": 883, + "Asia/Srednekolymsk": 884, + "Asia/Taipei": 885, + "Asia/Tashkent": 886, + "Asia/Tbilisi": 887, + "Asia/Tehran": 888, + "Asia/Thimphu": 889, + "Asia/Tokyo": 890, + "Asia/Tomsk": 891, + "Asia/Ulaanbaatar": 892, + "Asia/Urumqi": 893, + "Asia/Ust-Nera": 894, + "Asia/Vladivostok": 895, + "Asia/Yakutsk": 896, + "Asia/Yangon": 897, + "Asia/Yekaterinburg": 898, + "Asia/Yerevan": 899, + "Atlantic/Azores": 900, + "Atlantic/Bermuda": 901, + "Atlantic/Canary": 902, + "Atlantic/Cape_Verde": 903, + "Atlantic/Faroe": 904, + "Atlantic/Madeira": 905, + "Atlantic/Reykjavik": 906, + "Atlantic/South_Georgia": 907, + "Atlantic/Stanley": 908, + "Australia/Adelaide": 909, + "Australia/Brisbane": 910, + "Australia/Broken_Hill": 911, + "Australia/Darwin": 912, + "Australia/Eucla": 913, + "Australia/Hobart": 914, + "Australia/Lindeman": 915, + "Australia/Lord_Howe": 916, + "Australia/Melbourne": 917, + "Australia/Perth": 918, + "Australia/Sydney": 919, + "Etc/GMT": 920, + "Etc/UTC": 921, + "Europe/Amsterdam": 922, + "Europe/Andorra": 923, + "Europe/Astrakhan": 924, + "Europe/Athens": 925, + "Europe/Belgrade": 926, + "Europe/Berlin": 927, + "Europe/Brussels": 928, + "Europe/Bucharest": 929, + "Europe/Budapest": 930, + "Europe/Chisinau": 931, + "Europe/Copenhagen": 932, + "Europe/Dublin": 933, + "Europe/Gibraltar": 934, + "Europe/Helsinki": 935, + "Europe/Istanbul": 936, + "Europe/Kaliningrad": 937, + "Europe/Kiev": 938, + "Europe/Kirov": 939, + "Europe/Lisbon": 940, + "Europe/London": 941, + "Europe/Luxembourg": 942, + "Europe/Madrid": 943, + "Europe/Malta": 944, + "Europe/Minsk": 945, + "Europe/Monaco": 946, + "Europe/Moscow": 947, + "Europe/Oslo": 948, + "Europe/Paris": 949, + "Europe/Prague": 950, + "Europe/Riga": 951, + "Europe/Rome": 952, + "Europe/Samara": 953, + "Europe/Saratov": 954, + "Europe/Simferopol": 955, + "Europe/Sofia": 956, + "Europe/Stockholm": 957, + "Europe/Tallinn": 958, + "Europe/Tirane": 959, + "Europe/Ulyanovsk": 960, + "Europe/Uzhgorod": 961, + "Europe/Vienna": 962, + "Europe/Vilnius": 963, + "Europe/Volgograd": 964, + "Europe/Warsaw": 965, + "Europe/Zaporozhye": 966, + "Europe/Zurich": 967, + "Indian/Chagos": 968, + "Indian/Christmas": 969, + "Indian/Cocos": 970, + "Indian/Kerguelen": 971, + "Indian/Mahe": 972, + "Indian/Maldives": 973, + "Indian/Mauritius": 974, + "Indian/Reunion": 975, + "Pacific/Apia": 976, + "Pacific/Auckland": 977, + "Pacific/Bougainville": 978, + "Pacific/Chatham": 979, + "Pacific/Chuuk": 980, + "Pacific/Easter": 981, + "Pacific/Efate": 982, + "Pacific/Fakaofo": 983, + "Pacific/Fiji": 984, + "Pacific/Funafuti": 985, + "Pacific/Galapagos": 986, + "Pacific/Gambier": 987, + "Pacific/Guadalcanal": 988, + "Pacific/Guam": 989, + "Pacific/Honolulu": 990, + "Pacific/Kanton": 991, + "Pacific/Kiritimati": 992, + "Pacific/Kosrae": 993, + "Pacific/Kwajalein": 994, + "Pacific/Majuro": 995, + "Pacific/Marquesas": 996, + "Pacific/Nauru": 997, + "Pacific/Niue": 998, + "Pacific/Norfolk": 999, + "Pacific/Noumea": 1000, + "Pacific/Pago_Pago": 1001, + "Pacific/Palau": 1002, + "Pacific/Pitcairn": 1003, + "Pacific/Pohnpei": 1004, + "Pacific/Port_Moresby": 1005, + "Pacific/Rarotonga": 1006, + "Pacific/Tahiti": 1007, + "Pacific/Tarawa": 1008, + "Pacific/Tongatapu": 1009, + "Pacific/Wake": 1010, + "Pacific/Wallis": 1011, + "Africa/Accra": 672, + "Africa/Addis_Ababa": 685, + "Africa/Asmara": 685, + "Africa/Asmera": 685, + "Africa/Bamako": 672, + "Africa/Bangui": 682, + "Africa/Banjul": 672, + "Africa/Blantyre": 683, + "Africa/Brazzaville": 682, + "Africa/Bujumbura": 683, + "Africa/Conakry": 672, + "Africa/Dakar": 672, + "Africa/Dar_es_Salaam": 685, + "Africa/Djibouti": 685, + "Africa/Douala": 682, + "Africa/Freetown": 672, + "Africa/Gaborone": 683, + "Africa/Harare": 683, + "Africa/Kampala": 685, + "Africa/Kigali": 683, + "Africa/Kinshasa": 682, + "Africa/Libreville": 682, + "Africa/Lome": 672, + "Africa/Luanda": 682, + "Africa/Lubumbashi": 683, + "Africa/Lusaka": 683, + "Africa/Malabo": 682, + "Africa/Maseru": 679, + "Africa/Mbabane": 679, + "Africa/Mogadishu": 685, + "Africa/Niamey": 682, + "Africa/Nouakchott": 672, + "Africa/Ouagadougou": 672, + "Africa/Porto-Novo": 682, + "Africa/Timbuktu": 672, + "America/Anguilla": 789, + "America/Antigua": 789, + "America/Argentina/ComodRivadavia": 695, + "America/Aruba": 789, + "America/Atikokan": 783, + "America/Atka": 691, + "America/Blanc-Sablon": 789, + "America/Buenos_Aires": 694, + "America/Catamarca": 695, + "America/Cayman": 783, + "America/Coral_Harbour": 783, + "America/Cordoba": 696, + "America/Creston": 786, + "America/Curacao": 789, + "America/Dominica": 789, + "America/Ensenada": 808, + "America/Fort_Wayne": 743, + "America/Godthab": 781, + "America/Grenada": 789, + "America/Guadeloupe": 789, + "America/Indianapolis": 743, + "America/Jujuy": 697, + "America/Knox_IN": 744, + "America/Kralendijk": 789, + "America/Louisville": 755, + "America/Lower_Princes": 789, + "America/Marigot": 789, + "America/Mendoza": 699, + "America/Montreal": 809, + "America/Montserrat": 789, + "America/Nassau": 809, + "America/Port_of_Spain": 789, + "America/Porto_Acre": 796, + "America/Rosario": 696, + "America/Santa_Isabel": 808, + "America/Shiprock": 727, + "America/St_Barthelemy": 789, + "America/St_Kitts": 789, + "America/St_Lucia": 789, + "America/St_Thomas": 789, + "America/St_Vincent": 789, + "America/Tortola": 789, + "America/Virgin": 789, + "Antarctica/DumontDUrville": 1005, + "Antarctica/McMurdo": 977, + "Antarctica/South_Pole": 977, + "Antarctica/Syowa": 878, + "Arctic/Longyearbyen": 948, + "Asia/Aden": 878, + "Asia/Ashkhabad": 828, + "Asia/Bahrain": 875, + "Asia/Calcutta": 860, + "Asia/Chongqing": 882, + "Asia/Chungking": 882, + "Asia/Dacca": 841, + "Asia/Harbin": 882, + "Asia/Istanbul": 936, + "Asia/Kashgar": 893, + "Asia/Katmandu": 858, + "Asia/Kuwait": 878, + "Asia/Macao": 864, + "Asia/Muscat": 843, + "Asia/Phnom_Penh": 832, + "Asia/Rangoon": 897, + "Asia/Saigon": 848, + "Asia/Tel_Aviv": 854, + "Asia/Thimbu": 889, + "Asia/Ujung_Pandang": 866, + "Asia/Ulan_Bator": 892, + "Asia/Vientiane": 832, + "Atlantic/Faeroe": 904, + "Atlantic/Jan_Mayen": 948, + "Atlantic/St_Helena": 672, + "Australia/ACT": 919, + "Australia/Canberra": 919, + "Australia/Currie": 914, + "Australia/LHI": 916, + "Australia/NSW": 919, + "Australia/North": 912, + "Australia/Queensland": 910, + "Australia/South": 909, + "Australia/Tasmania": 914, + "Australia/Victoria": 917, + "Australia/West": 918, + "Australia/Yancowinna": 911, + "Brazil/Acre": 796, + "Brazil/DeNoronha": 777, + "Brazil/East": 800, + "Brazil/West": 762, + "Canada/Atlantic": 740, + "Canada/Central": 812, + "Canada/Eastern": 809, + "Canada/Mountain": 729, + "Canada/Newfoundland": 803, + "Canada/Pacific": 810, + "Canada/Saskatchewan": 794, + "Canada/Yukon": 811, + "Chile/Continental": 798, + "Chile/EasterIsland": 981, + "Cuba": 741, + "Egypt": 675, + "Eire": 933, + "Etc/GMT+0": 920, + "Etc/GMT-0": 920, + "Etc/GMT0": 920, + "Etc/Greenwich": 920, + "Etc/UCT": 921, + "Etc/Universal": 921, + "Etc/Zulu": 921, + "Europe/Belfast": 941, + "Europe/Bratislava": 950, + "Europe/Busingen": 967, + "Europe/Guernsey": 941, + "Europe/Isle_of_Man": 941, + "Europe/Jersey": 941, + "Europe/Ljubljana": 926, + "Europe/Mariehamn": 935, + "Europe/Nicosia": 868, + "Europe/Podgorica": 926, + "Europe/San_Marino": 952, + "Europe/Sarajevo": 926, + "Europe/Skopje": 926, + "Europe/Tiraspol": 931, + "Europe/Vaduz": 967, + "Europe/Vatican": 952, + "Europe/Zagreb": 926, + "GB": 941, + "GB-Eire": 941, + "GMT+0": 920, + "GMT-0": 920, + "GMT0": 920, + "Greenwich": 920, + "Hongkong": 849, + "Iceland": 906, + "Indian/Antananarivo": 685, + "Indian/Comoro": 685, + "Indian/Mayotte": 685, + "Iran": 888, + "Israel": 854, + "Jamaica": 753, + "Japan": 890, + "Kwajalein": 994, + "Libya": 688, + "Mexico/BajaNorte": 808, + "Mexico/BajaSur": 765, + "Mexico/General": 769, + "NZ": 977, + "NZ-CHAT": 979, + "Navajo": 727, + "PRC": 882, + "Pacific/Enderbury": 991, + "Pacific/Johnston": 990, + "Pacific/Midway": 1001, + "Pacific/Ponape": 1004, + "Pacific/Saipan": 989, + "Pacific/Samoa": 1001, + "Pacific/Truk": 980, + "Pacific/Yap": 980, + "Poland": 965, + "Portugal": 940, + "ROC": 885, + "ROK": 881, + "Singapore": 883, + "Turkey": 936, + "UCT": 921, + "US/Alaska": 692, + "US/Aleutian": 691, + "US/Arizona": 786, + "US/Central": 720, + "US/East-Indiana": 743, + "US/Eastern": 774, + "US/Hawaii": 990, + "US/Indiana-Starke": 744, + "US/Michigan": 728, + "US/Mountain": 727, + "US/Pacific": 759, + "US/Samoa": 1001, + "Universal": 921, + "W-SU": 947, + "Zulu": 921, } timezoneAbbrevInfo = { - "A" : {"offset" : 60, "category" : TZ_MILITARY}, - "B" : {"offset" : 120, "category" : TZ_MILITARY}, - "C" : {"offset" : 180, "category" : TZ_MILITARY}, - "D" : {"offset" : 240, "category" : TZ_MILITARY}, - "E" : {"offset" : 300, "category" : TZ_MILITARY}, - "F" : {"offset" : 360, "category" : TZ_MILITARY}, - "G" : {"offset" : 420, "category" : TZ_MILITARY}, - "H" : {"offset" : 480, "category" : TZ_MILITARY}, - "I" : {"offset" : 540, "category" : TZ_MILITARY}, - "K" : {"offset" : 600, "category" : TZ_MILITARY}, - "L" : {"offset" : 660, "category" : TZ_MILITARY}, - "M" : {"offset" : 720, "category" : TZ_MILITARY}, - "N" : {"offset" : -60, "category" : TZ_MILITARY}, - "O" : {"offset" : -120, "category" : TZ_MILITARY}, - "P" : {"offset" : -180, "category" : TZ_MILITARY}, - "Q" : {"offset" : -240, "category" : TZ_MILITARY}, - "R" : {"offset" : -300, "category" : TZ_MILITARY}, - "S" : {"offset" : -360, "category" : TZ_MILITARY}, - "T" : {"offset" : -420, "category" : TZ_MILITARY}, - "U" : {"offset" : -480, "category" : TZ_MILITARY}, - "V" : {"offset" : -540, "category" : TZ_MILITARY}, - "W" : {"offset" : -600, "category" : TZ_MILITARY}, - "X" : {"offset" : -660, "category" : TZ_MILITARY}, - "Y" : {"offset" : -720, "category" : TZ_MILITARY}, - "Z" : {"offset" : 0, "category" : TZ_MILITARY}, - "AT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "BT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "CT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "ET" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "GT" : {"offset" : 0, "category" : 0}, - "IT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "KT" : {"offset" : 540, "category" : 0}, - "MT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "PT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "ST" : {"offset" : 780, "category" : 0}, - "UT" : {"offset" : 0, "category" : TZ_UTC|TZ_RFC}, - "WT" : {"offset" : 0, "category" : 0}, - "ACT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "ADT" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "AET" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "AFT" : {"offset" : 270, "category" : 0}, - "AMT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "AoE" : {"offset" : -720, "category" : 0}, - "ART" : {"offset" : -180, "category" : 0}, - "AST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "AZT" : {"offset" : 240, "category" : 0}, - "BDT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "BNT" : {"offset" : 480, "category" : 0}, - "BOT" : {"offset" : -240, "category" : 0}, - "BRT" : {"offset" : -180, "category" : 0}, - "BST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "BTT" : {"offset" : 360, "category" : 0}, - "CAT" : {"offset" : 120, "category" : 0}, - "CCT" : {"offset" : 390, "category" : 0}, - "CDT" : {"offset" : -300, "category" : TZ_RFC|TZ_AMBIGUOUS|TZ_DST}, - "CET" : {"offset" : 60, "category" : 0}, - "CIT" : {"offset" : -300, "category" : 0}, - "CKT" : {"offset" : -600, "category" : 0}, - "CLT" : {"offset" : -180, "category" : 0}, - "COT" : {"offset" : -300, "category" : 0}, - "CST" : {"offset" : -360, "category" : TZ_RFC|TZ_AMBIGUOUS}, - "CVT" : {"offset" : -60, "category" : 0}, - "CXT" : {"offset" : 420, "category" : 0}, - "EAT" : {"offset" : 180, "category" : 0}, - "ECT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "EDT" : {"offset" : -240, "category" : TZ_RFC|TZ_AMBIGUOUS|TZ_DST}, - "EET" : {"offset" : 120, "category" : 0}, - "EGT" : {"offset" : -60, "category" : 0}, - "EST" : {"offset" : -300, "category" : TZ_RFC|TZ_AMBIGUOUS}, - "FET" : {"offset" : 180, "category" : 0}, - "FJT" : {"offset" : 720, "category" : 0}, - "FKT" : {"offset" : -240, "category" : 0}, - "FNT" : {"offset" : -120, "category" : 0}, - "GET" : {"offset" : 240, "category" : 0}, - "GFT" : {"offset" : -180, "category" : 0}, - "GMT" : {"offset" : 0, "category" : TZ_UTC|TZ_RFC}, - "GST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "GYT" : {"offset" : -240, "category" : 0}, - "HAA" : {"offset" : -180, "category" : 0}, - "HAC" : {"offset" : -300, "category" : 0}, - "HAE" : {"offset" : -240, "category" : 0}, - "HAP" : {"offset" : -420, "category" : 0}, - "HAR" : {"offset" : -360, "category" : 0}, - "HAT" : {"offset" : -90, "category" : 0}, - "HDT" : {"offset" : -540, "category" : TZ_DST}, - "HKT" : {"offset" : 480, "category" : 0}, - "HLV" : {"offset" : -210, "category" : 0}, - "HNA" : {"offset" : -240, "category" : 0}, - "HNC" : {"offset" : -360, "category" : 0}, - "HNE" : {"offset" : -300, "category" : 0}, - "HNP" : {"offset" : -480, "category" : 0}, - "HNR" : {"offset" : -420, "category" : 0}, - "HNT" : {"offset" : -150, "category" : 0}, - "HST" : {"offset" : -600, "category" : 0}, - "ICT" : {"offset" : 420, "category" : 0}, - "IDT" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "IOT" : {"offset" : 360, "category" : 0}, - "IST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "JST" : {"offset" : 540, "category" : 0}, - "KGT" : {"offset" : 360, "category" : 0}, - "KIT" : {"offset" : 300, "category" : 0}, - "KST" : {"offset" : 540, "category" : 0}, - "MCK" : {"offset" : 180, "category" : 0}, - "MDT" : {"offset" : -360, "category" : TZ_RFC|TZ_DST}, - "MEZ" : {"offset" : 60, "category" : 0}, - "MHT" : {"offset" : 720, "category" : 0}, - "MMT" : {"offset" : 390, "category" : 0}, - "MSD" : {"offset" : 240, "category" : TZ_DST}, - "MSK" : {"offset" : 180, "category" : 0}, - "MST" : {"offset" : -420, "category" : TZ_RFC|TZ_AMBIGUOUS}, - "MUT" : {"offset" : 240, "category" : 0}, - "MVT" : {"offset" : 300, "category" : 0}, - "MYT" : {"offset" : 480, "category" : 0}, - "NCT" : {"offset" : 660, "category" : 0}, - "NDT" : {"offset" : -90, "category" : TZ_DST}, - "NFT" : {"offset" : 660, "category" : 0}, - "NPT" : {"offset" : 345, "category" : 0}, - "NRT" : {"offset" : 720, "category" : 0}, - "NST" : {"offset" : -150, "category" : 0}, - "NUT" : {"offset" : -660, "category" : 0}, - "OEZ" : {"offset" : 120, "category" : 0}, - "PDT" : {"offset" : -420, "category" : TZ_RFC|TZ_DST}, - "PET" : {"offset" : -300, "category" : 0}, - "PGT" : {"offset" : 600, "category" : 0}, - "PHT" : {"offset" : 480, "category" : 0}, - "PKT" : {"offset" : 300, "category" : 0}, - "PST" : {"offset" : -480, "category" : TZ_RFC|TZ_AMBIGUOUS}, - "PWT" : {"offset" : 540, "category" : 0}, - "PYT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "RET" : {"offset" : 240, "category" : 0}, - "SBT" : {"offset" : 660, "category" : 0}, - "SCT" : {"offset" : 240, "category" : 0}, - "SGT" : {"offset" : 480, "category" : 0}, - "SRT" : {"offset" : -180, "category" : 0}, - "SST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "TFT" : {"offset" : 300, "category" : 0}, - "TJT" : {"offset" : 300, "category" : 0}, - "TKT" : {"offset" : 780, "category" : 0}, - "TLT" : {"offset" : 540, "category" : 0}, - "TMT" : {"offset" : 300, "category" : 0}, - "TOT" : {"offset" : 780, "category" : 0}, - "TRT" : {"offset" : 180, "category" : 0}, - "TVT" : {"offset" : 720, "category" : 0}, - "UTC" : {"offset" : 0, "category" : TZ_UTC|TZ_RFC}, - "UYT" : {"offset" : -180, "category" : 0}, - "UZT" : {"offset" : 300, "category" : 0}, - "VET" : {"offset" : -210, "category" : 0}, - "VUT" : {"offset" : 660, "category" : 0}, - "WAT" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "WDT" : {"offset" : 540, "category" : TZ_DST}, - "WET" : {"offset" : 0, "category" : 0}, - "WEZ" : {"offset" : 0, "category" : 0}, - "WFT" : {"offset" : 720, "category" : 0}, - "WGT" : {"offset" : -180, "category" : 0}, - "WIB" : {"offset" : 420, "category" : 0}, - "WIT" : {"offset" : 540, "category" : 0}, - "WST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "ACDT" : {"offset" : 630, "category" : TZ_DST}, - "ACST" : {"offset" : 570, "category" : 0}, - "ADST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "AEDT" : {"offset" : 660, "category" : TZ_DST}, - "AEST" : {"offset" : 600, "category" : 0}, - "AKDT" : {"offset" : -480, "category" : TZ_DST}, - "AKST" : {"offset" : -540, "category" : 0}, - "ALMT" : {"offset" : 360, "category" : 0}, - "AMDT" : {"offset" : 300, "category" : TZ_DST}, - "AMST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "ANAT" : {"offset" : 720, "category" : 0}, - "AQTT" : {"offset" : 300, "category" : 0}, - "AWDT" : {"offset" : 540, "category" : TZ_DST}, - "AWST" : {"offset" : 480, "category" : 0}, - "AZOT" : {"offset" : -60, "category" : 0}, - "AZST" : {"offset" : 300, "category" : TZ_DST}, - "BDST" : {"offset" : 60, "category" : TZ_DST}, - "BRST" : {"offset" : -120, "category" : TZ_DST}, - "CAST" : {"offset" : 480, "category" : 0}, - "CDST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "CEDT" : {"offset" : 120, "category" : TZ_DST}, - "CEST" : {"offset" : 120, "category" : TZ_DST}, - "CHOT" : {"offset" : 480, "category" : 0}, - "ChST" : {"offset" : 600, "category" : 0}, - "CHUT" : {"offset" : 600, "category" : 0}, - "CIST" : {"offset" : -300, "category" : 0}, - "CLDT" : {"offset" : -180, "category" : TZ_DST}, - "CLST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "DAVT" : {"offset" : 420, "category" : 0}, - "DDUT" : {"offset" : 600, "category" : 0}, - "EADT" : {"offset" : -300, "category" : TZ_DST}, - "EAST" : {"offset" : -300, "category" : 0}, - "ECST" : {"offset" : 120, "category" : TZ_DST}, - "EDST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "EEDT" : {"offset" : 180, "category" : TZ_DST}, - "EEST" : {"offset" : 180, "category" : TZ_DST}, - "EGST" : {"offset" : 0, "category" : TZ_DST}, - "FJDT" : {"offset" : 780, "category" : TZ_DST}, - "FJST" : {"offset" : 780, "category" : TZ_DST}, - "FKDT" : {"offset" : -180, "category" : TZ_DST}, - "FKST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "GALT" : {"offset" : -360, "category" : 0}, - "GAMT" : {"offset" : -540, "category" : 0}, - "GILT" : {"offset" : 720, "category" : 0}, - "HADT" : {"offset" : -540, "category" : TZ_DST}, - "HAST" : {"offset" : -600, "category" : 0}, - "HOVT" : {"offset" : 420, "category" : 0}, - "IRDT" : {"offset" : 270, "category" : TZ_DST}, - "IRKT" : {"offset" : 480, "category" : 0}, - "IRST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "KOST" : {"offset" : 660, "category" : 0}, - "KRAT" : {"offset" : 420, "category" : 0}, - "KUYT" : {"offset" : 240, "category" : 0}, - "LHDT" : {"offset" : 660, "category" : TZ_DST}, - "LHST" : {"offset" : 630, "category" : 0}, - "LINT" : {"offset" : 840, "category" : 0}, - "MAGT" : {"offset" : 600, "category" : 0}, - "MART" : {"offset" : -510, "category" : 0}, - "MAWT" : {"offset" : 300, "category" : 0}, - "MDST" : {"offset" : -360, "category" : TZ_DST}, - "MESZ" : {"offset" : 120, "category" : 0}, - "NFDT" : {"offset" : 720, "category" : TZ_DST}, - "NOVT" : {"offset" : 360, "category" : 0}, - "NZDT" : {"offset" : 780, "category" : TZ_DST}, - "NZST" : {"offset" : 720, "category" : 0}, - "OESZ" : {"offset" : 180, "category" : 0}, - "OMST" : {"offset" : 360, "category" : 0}, - "ORAT" : {"offset" : 300, "category" : 0}, - "PDST" : {"offset" : -420, "category" : TZ_DST}, - "PETT" : {"offset" : 720, "category" : 0}, - "PHOT" : {"offset" : 780, "category" : 0}, - "PMDT" : {"offset" : -120, "category" : TZ_DST}, - "PMST" : {"offset" : -180, "category" : 0}, - "PONT" : {"offset" : 660, "category" : 0}, - "PYST" : {"offset" : 0, "category" : TZ_AMBIGUOUS}, - "QYZT" : {"offset" : 360, "category" : 0}, - "ROTT" : {"offset" : -180, "category" : 0}, - "SAKT" : {"offset" : 600, "category" : 0}, - "SAMT" : {"offset" : 240, "category" : 0}, - "SAST" : {"offset" : 120, "category" : 0}, - "SRET" : {"offset" : 660, "category" : 0}, - "SYOT" : {"offset" : 180, "category" : 0}, - "TAHT" : {"offset" : -600, "category" : 0}, - "TOST" : {"offset" : 840, "category" : TZ_DST}, - "ULAT" : {"offset" : 480, "category" : 0}, - "UYST" : {"offset" : -120, "category" : TZ_DST}, - "VLAT" : {"offset" : 600, "category" : 0}, - "VOST" : {"offset" : 360, "category" : 0}, - "WAKT" : {"offset" : 720, "category" : 0}, - "WAST" : {"offset" : 120, "category" : TZ_DST}, - "WEDT" : {"offset" : 60, "category" : TZ_DST}, - "WEST" : {"offset" : 60, "category" : TZ_DST}, - "WESZ" : {"offset" : 60, "category" : 0}, - "WGST" : {"offset" : -120, "category" : TZ_DST}, - "WITA" : {"offset" : 480, "category" : 0}, - "YAKT" : {"offset" : 540, "category" : 0}, - "YAPT" : {"offset" : 600, "category" : 0}, - "YEKT" : {"offset" : 300, "category" : 0}, - "ACWST" : {"offset" : 525, "category" : 0}, - "ANAST" : {"offset" : 720, "category" : TZ_DST}, - "AZODT" : {"offset" : 0, "category" : TZ_DST}, - "AZOST" : {"offset" : 0, "category" : TZ_AMBIGUOUS|TZ_DST}, - "CHADT" : {"offset" : 825, "category" : TZ_DST}, - "CHAST" : {"offset" : 765, "category" : 0}, - "CHODT" : {"offset" : 540, "category" : TZ_DST}, - "CHOST" : {"offset" : 540, "category" : TZ_DST}, - "CIDST" : {"offset" : -240, "category" : TZ_DST}, - "EASST" : {"offset" : -300, "category" : TZ_DST}, - "EFATE" : {"offset" : 660, "category" : 0}, - "HOVDT" : {"offset" : 480, "category" : TZ_DST}, - "HOVST" : {"offset" : 480, "category" : TZ_DST}, - "IRKST" : {"offset" : 540, "category" : TZ_DST}, - "KRAST" : {"offset" : 480, "category" : TZ_DST}, - "MAGST" : {"offset" : 720, "category" : TZ_DST}, - "NACDT" : {"offset" : -300, "category" : TZ_DST}, - "NACST" : {"offset" : -360, "category" : 0}, - "NAEDT" : {"offset" : -240, "category" : TZ_DST}, - "NAEST" : {"offset" : -300, "category" : 0}, - "NAMDT" : {"offset" : -360, "category" : TZ_DST}, - "NAMST" : {"offset" : -420, "category" : 0}, - "NAPDT" : {"offset" : -420, "category" : TZ_DST}, - "NAPST" : {"offset" : -480, "category" : 0}, - "NOVST" : {"offset" : 420, "category" : TZ_DST}, - "OMSST" : {"offset" : 420, "category" : TZ_DST}, - "PETST" : {"offset" : 720, "category" : TZ_DST}, - "SAMST" : {"offset" : 240, "category" : TZ_DST}, - "ULAST" : {"offset" : 540, "category" : TZ_DST}, - "VLAST" : {"offset" : 660, "category" : TZ_DST}, - "WARST" : {"offset" : -180, "category" : TZ_DST}, - "YAKST" : {"offset" : 600, "category" : TZ_DST}, - "YEKST" : {"offset" : 360, "category" : TZ_DST}, - "CHODST" : {"offset" : 540, "category" : TZ_DST}, - "HOVDST" : {"offset" : 480, "category" : TZ_DST}, + "A": {"offset": 60, "category": TZ_MILITARY}, + "B": {"offset": 120, "category": TZ_MILITARY}, + "C": {"offset": 180, "category": TZ_MILITARY}, + "D": {"offset": 240, "category": TZ_MILITARY}, + "E": {"offset": 300, "category": TZ_MILITARY}, + "F": {"offset": 360, "category": TZ_MILITARY}, + "G": {"offset": 420, "category": TZ_MILITARY}, + "H": {"offset": 480, "category": TZ_MILITARY}, + "I": {"offset": 540, "category": TZ_MILITARY}, + "K": {"offset": 600, "category": TZ_MILITARY}, + "L": {"offset": 660, "category": TZ_MILITARY}, + "M": {"offset": 720, "category": TZ_MILITARY}, + "N": {"offset": -60, "category": TZ_MILITARY}, + "O": {"offset": -120, "category": TZ_MILITARY}, + "P": {"offset": -180, "category": TZ_MILITARY}, + "Q": {"offset": -240, "category": TZ_MILITARY}, + "R": {"offset": -300, "category": TZ_MILITARY}, + "S": {"offset": -360, "category": TZ_MILITARY}, + "T": {"offset": -420, "category": TZ_MILITARY}, + "U": {"offset": -480, "category": TZ_MILITARY}, + "V": {"offset": -540, "category": TZ_MILITARY}, + "W": {"offset": -600, "category": TZ_MILITARY}, + "X": {"offset": -660, "category": TZ_MILITARY}, + "Y": {"offset": -720, "category": TZ_MILITARY}, + "Z": {"offset": 0, "category": TZ_MILITARY}, + "AT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "BT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "CT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "ET": {"offset": 0, "category": TZ_AMBIGUOUS}, + "GT": {"offset": 0, "category": 0}, + "IT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "KT": {"offset": 540, "category": 0}, + "MT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "PT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "ST": {"offset": 780, "category": 0}, + "UT": {"offset": 0, "category": TZ_UTC | TZ_RFC}, + "WT": {"offset": 0, "category": 0}, + "ACT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "ADT": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "AET": {"offset": 0, "category": TZ_AMBIGUOUS}, + "AFT": {"offset": 270, "category": 0}, + "AMT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "AoE": {"offset": -720, "category": 0}, + "ART": {"offset": -180, "category": 0}, + "AST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "AZT": {"offset": 240, "category": 0}, + "BDT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "BNT": {"offset": 480, "category": 0}, + "BOT": {"offset": -240, "category": 0}, + "BRT": {"offset": -180, "category": 0}, + "BST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "BTT": {"offset": 360, "category": 0}, + "CAT": {"offset": 120, "category": 0}, + "CCT": {"offset": 390, "category": 0}, + "CDT": {"offset": -300, "category": TZ_RFC | TZ_AMBIGUOUS | TZ_DST}, + "CET": {"offset": 60, "category": 0}, + "CIT": {"offset": -300, "category": 0}, + "CKT": {"offset": -600, "category": 0}, + "CLT": {"offset": -180, "category": 0}, + "COT": {"offset": -300, "category": 0}, + "CST": {"offset": -360, "category": TZ_RFC | TZ_AMBIGUOUS}, + "CVT": {"offset": -60, "category": 0}, + "CXT": {"offset": 420, "category": 0}, + "EAT": {"offset": 180, "category": 0}, + "ECT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "EDT": {"offset": -240, "category": TZ_RFC | TZ_AMBIGUOUS | TZ_DST}, + "EET": {"offset": 120, "category": 0}, + "EGT": {"offset": -60, "category": 0}, + "EST": {"offset": -300, "category": TZ_RFC | TZ_AMBIGUOUS}, + "FET": {"offset": 180, "category": 0}, + "FJT": {"offset": 720, "category": 0}, + "FKT": {"offset": -240, "category": 0}, + "FNT": {"offset": -120, "category": 0}, + "GET": {"offset": 240, "category": 0}, + "GFT": {"offset": -180, "category": 0}, + "GMT": {"offset": 0, "category": TZ_UTC | TZ_RFC}, + "GST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "GYT": {"offset": -240, "category": 0}, + "HAA": {"offset": -180, "category": 0}, + "HAC": {"offset": -300, "category": 0}, + "HAE": {"offset": -240, "category": 0}, + "HAP": {"offset": -420, "category": 0}, + "HAR": {"offset": -360, "category": 0}, + "HAT": {"offset": -90, "category": 0}, + "HDT": {"offset": -540, "category": TZ_DST}, + "HKT": {"offset": 480, "category": 0}, + "HLV": {"offset": -210, "category": 0}, + "HNA": {"offset": -240, "category": 0}, + "HNC": {"offset": -360, "category": 0}, + "HNE": {"offset": -300, "category": 0}, + "HNP": {"offset": -480, "category": 0}, + "HNR": {"offset": -420, "category": 0}, + "HNT": {"offset": -150, "category": 0}, + "HST": {"offset": -600, "category": 0}, + "ICT": {"offset": 420, "category": 0}, + "IDT": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "IOT": {"offset": 360, "category": 0}, + "IST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "JST": {"offset": 540, "category": 0}, + "KGT": {"offset": 360, "category": 0}, + "KIT": {"offset": 300, "category": 0}, + "KST": {"offset": 540, "category": 0}, + "MCK": {"offset": 180, "category": 0}, + "MDT": {"offset": -360, "category": TZ_RFC | TZ_DST}, + "MEZ": {"offset": 60, "category": 0}, + "MHT": {"offset": 720, "category": 0}, + "MMT": {"offset": 390, "category": 0}, + "MSD": {"offset": 240, "category": TZ_DST}, + "MSK": {"offset": 180, "category": 0}, + "MST": {"offset": -420, "category": TZ_RFC | TZ_AMBIGUOUS}, + "MUT": {"offset": 240, "category": 0}, + "MVT": {"offset": 300, "category": 0}, + "MYT": {"offset": 480, "category": 0}, + "NCT": {"offset": 660, "category": 0}, + "NDT": {"offset": -90, "category": TZ_DST}, + "NFT": {"offset": 660, "category": 0}, + "NPT": {"offset": 345, "category": 0}, + "NRT": {"offset": 720, "category": 0}, + "NST": {"offset": -150, "category": 0}, + "NUT": {"offset": -660, "category": 0}, + "OEZ": {"offset": 120, "category": 0}, + "PDT": {"offset": -420, "category": TZ_RFC | TZ_DST}, + "PET": {"offset": -300, "category": 0}, + "PGT": {"offset": 600, "category": 0}, + "PHT": {"offset": 480, "category": 0}, + "PKT": {"offset": 300, "category": 0}, + "PST": {"offset": -480, "category": TZ_RFC | TZ_AMBIGUOUS}, + "PWT": {"offset": 540, "category": 0}, + "PYT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "RET": {"offset": 240, "category": 0}, + "SBT": {"offset": 660, "category": 0}, + "SCT": {"offset": 240, "category": 0}, + "SGT": {"offset": 480, "category": 0}, + "SRT": {"offset": -180, "category": 0}, + "SST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "TFT": {"offset": 300, "category": 0}, + "TJT": {"offset": 300, "category": 0}, + "TKT": {"offset": 780, "category": 0}, + "TLT": {"offset": 540, "category": 0}, + "TMT": {"offset": 300, "category": 0}, + "TOT": {"offset": 780, "category": 0}, + "TRT": {"offset": 180, "category": 0}, + "TVT": {"offset": 720, "category": 0}, + "UTC": {"offset": 0, "category": TZ_UTC | TZ_RFC}, + "UYT": {"offset": -180, "category": 0}, + "UZT": {"offset": 300, "category": 0}, + "VET": {"offset": -210, "category": 0}, + "VUT": {"offset": 660, "category": 0}, + "WAT": {"offset": 0, "category": TZ_AMBIGUOUS}, + "WDT": {"offset": 540, "category": TZ_DST}, + "WET": {"offset": 0, "category": 0}, + "WEZ": {"offset": 0, "category": 0}, + "WFT": {"offset": 720, "category": 0}, + "WGT": {"offset": -180, "category": 0}, + "WIB": {"offset": 420, "category": 0}, + "WIT": {"offset": 540, "category": 0}, + "WST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "ACDT": {"offset": 630, "category": TZ_DST}, + "ACST": {"offset": 570, "category": 0}, + "ADST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "AEDT": {"offset": 660, "category": TZ_DST}, + "AEST": {"offset": 600, "category": 0}, + "AKDT": {"offset": -480, "category": TZ_DST}, + "AKST": {"offset": -540, "category": 0}, + "ALMT": {"offset": 360, "category": 0}, + "AMDT": {"offset": 300, "category": TZ_DST}, + "AMST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "ANAT": {"offset": 720, "category": 0}, + "AQTT": {"offset": 300, "category": 0}, + "AWDT": {"offset": 540, "category": TZ_DST}, + "AWST": {"offset": 480, "category": 0}, + "AZOT": {"offset": -60, "category": 0}, + "AZST": {"offset": 300, "category": TZ_DST}, + "BDST": {"offset": 60, "category": TZ_DST}, + "BRST": {"offset": -120, "category": TZ_DST}, + "CAST": {"offset": 480, "category": 0}, + "CDST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "CEDT": {"offset": 120, "category": TZ_DST}, + "CEST": {"offset": 120, "category": TZ_DST}, + "CHOT": {"offset": 480, "category": 0}, + "ChST": {"offset": 600, "category": 0}, + "CHUT": {"offset": 600, "category": 0}, + "CIST": {"offset": -300, "category": 0}, + "CLDT": {"offset": -180, "category": TZ_DST}, + "CLST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "DAVT": {"offset": 420, "category": 0}, + "DDUT": {"offset": 600, "category": 0}, + "EADT": {"offset": -300, "category": TZ_DST}, + "EAST": {"offset": -300, "category": 0}, + "ECST": {"offset": 120, "category": TZ_DST}, + "EDST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "EEDT": {"offset": 180, "category": TZ_DST}, + "EEST": {"offset": 180, "category": TZ_DST}, + "EGST": {"offset": 0, "category": TZ_DST}, + "FJDT": {"offset": 780, "category": TZ_DST}, + "FJST": {"offset": 780, "category": TZ_DST}, + "FKDT": {"offset": -180, "category": TZ_DST}, + "FKST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "GALT": {"offset": -360, "category": 0}, + "GAMT": {"offset": -540, "category": 0}, + "GILT": {"offset": 720, "category": 0}, + "HADT": {"offset": -540, "category": TZ_DST}, + "HAST": {"offset": -600, "category": 0}, + "HOVT": {"offset": 420, "category": 0}, + "IRDT": {"offset": 270, "category": TZ_DST}, + "IRKT": {"offset": 480, "category": 0}, + "IRST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "KOST": {"offset": 660, "category": 0}, + "KRAT": {"offset": 420, "category": 0}, + "KUYT": {"offset": 240, "category": 0}, + "LHDT": {"offset": 660, "category": TZ_DST}, + "LHST": {"offset": 630, "category": 0}, + "LINT": {"offset": 840, "category": 0}, + "MAGT": {"offset": 600, "category": 0}, + "MART": {"offset": -510, "category": 0}, + "MAWT": {"offset": 300, "category": 0}, + "MDST": {"offset": -360, "category": TZ_DST}, + "MESZ": {"offset": 120, "category": 0}, + "NFDT": {"offset": 720, "category": TZ_DST}, + "NOVT": {"offset": 360, "category": 0}, + "NZDT": {"offset": 780, "category": TZ_DST}, + "NZST": {"offset": 720, "category": 0}, + "OESZ": {"offset": 180, "category": 0}, + "OMST": {"offset": 360, "category": 0}, + "ORAT": {"offset": 300, "category": 0}, + "PDST": {"offset": -420, "category": TZ_DST}, + "PETT": {"offset": 720, "category": 0}, + "PHOT": {"offset": 780, "category": 0}, + "PMDT": {"offset": -120, "category": TZ_DST}, + "PMST": {"offset": -180, "category": 0}, + "PONT": {"offset": 660, "category": 0}, + "PYST": {"offset": 0, "category": TZ_AMBIGUOUS}, + "QYZT": {"offset": 360, "category": 0}, + "ROTT": {"offset": -180, "category": 0}, + "SAKT": {"offset": 600, "category": 0}, + "SAMT": {"offset": 240, "category": 0}, + "SAST": {"offset": 120, "category": 0}, + "SRET": {"offset": 660, "category": 0}, + "SYOT": {"offset": 180, "category": 0}, + "TAHT": {"offset": -600, "category": 0}, + "TOST": {"offset": 840, "category": TZ_DST}, + "ULAT": {"offset": 480, "category": 0}, + "UYST": {"offset": -120, "category": TZ_DST}, + "VLAT": {"offset": 600, "category": 0}, + "VOST": {"offset": 360, "category": 0}, + "WAKT": {"offset": 720, "category": 0}, + "WAST": {"offset": 120, "category": TZ_DST}, + "WEDT": {"offset": 60, "category": TZ_DST}, + "WEST": {"offset": 60, "category": TZ_DST}, + "WESZ": {"offset": 60, "category": 0}, + "WGST": {"offset": -120, "category": TZ_DST}, + "WITA": {"offset": 480, "category": 0}, + "YAKT": {"offset": 540, "category": 0}, + "YAPT": {"offset": 600, "category": 0}, + "YEKT": {"offset": 300, "category": 0}, + "ACWST": {"offset": 525, "category": 0}, + "ANAST": {"offset": 720, "category": TZ_DST}, + "AZODT": {"offset": 0, "category": TZ_DST}, + "AZOST": {"offset": 0, "category": TZ_AMBIGUOUS | TZ_DST}, + "CHADT": {"offset": 825, "category": TZ_DST}, + "CHAST": {"offset": 765, "category": 0}, + "CHODT": {"offset": 540, "category": TZ_DST}, + "CHOST": {"offset": 540, "category": TZ_DST}, + "CIDST": {"offset": -240, "category": TZ_DST}, + "EASST": {"offset": -300, "category": TZ_DST}, + "EFATE": {"offset": 660, "category": 0}, + "HOVDT": {"offset": 480, "category": TZ_DST}, + "HOVST": {"offset": 480, "category": TZ_DST}, + "IRKST": {"offset": 540, "category": TZ_DST}, + "KRAST": {"offset": 480, "category": TZ_DST}, + "MAGST": {"offset": 720, "category": TZ_DST}, + "NACDT": {"offset": -300, "category": TZ_DST}, + "NACST": {"offset": -360, "category": 0}, + "NAEDT": {"offset": -240, "category": TZ_DST}, + "NAEST": {"offset": -300, "category": 0}, + "NAMDT": {"offset": -360, "category": TZ_DST}, + "NAMST": {"offset": -420, "category": 0}, + "NAPDT": {"offset": -420, "category": TZ_DST}, + "NAPST": {"offset": -480, "category": 0}, + "NOVST": {"offset": 420, "category": TZ_DST}, + "OMSST": {"offset": 420, "category": TZ_DST}, + "PETST": {"offset": 720, "category": TZ_DST}, + "SAMST": {"offset": 240, "category": TZ_DST}, + "ULAST": {"offset": 540, "category": TZ_DST}, + "VLAST": {"offset": 660, "category": TZ_DST}, + "WARST": {"offset": -180, "category": TZ_DST}, + "YAKST": {"offset": 600, "category": TZ_DST}, + "YEKST": {"offset": 360, "category": TZ_DST}, + "CHODST": {"offset": 540, "category": TZ_DST}, + "HOVDST": {"offset": 480, "category": TZ_DST}, } diff --git a/tarantool/msgpack_ext/unpacker.py b/tarantool/msgpack_ext/unpacker.py index 956df6ca..e7c1b574 100644 --- a/tarantool/msgpack_ext/unpacker.py +++ b/tarantool/msgpack_ext/unpacker.py @@ -11,13 +11,14 @@ import tarantool.msgpack_ext.interval as ext_interval decoders = { - ext_decimal.EXT_ID : ext_decimal.decode , - ext_uuid.EXT_ID : ext_uuid.decode , - ext_error.EXT_ID : ext_error.decode , + ext_decimal.EXT_ID: ext_decimal.decode, + ext_uuid.EXT_ID: ext_uuid.decode, + ext_error.EXT_ID: ext_error.decode, ext_datetime.EXT_ID: ext_datetime.decode, ext_interval.EXT_ID: ext_interval.decode, } + def ext_hook(code, data, unpacker=None): """ :class:`msgpack.Unpacker` decoder. diff --git a/tarantool/msgpack_ext/uuid.py b/tarantool/msgpack_ext/uuid.py index 91b4ac94..859d096c 100644 --- a/tarantool/msgpack_ext/uuid.py +++ b/tarantool/msgpack_ext/uuid.py @@ -20,6 +20,7 @@ `uuid`_ type id. """ + def encode(obj, _): """ Encode an UUID object. @@ -33,6 +34,7 @@ def encode(obj, _): return obj.bytes + def decode(data, _): """ Decode an UUID object. diff --git a/tarantool/request.py b/tarantool/request.py index 8773602a..bdbbe68a 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -61,6 +61,7 @@ from tarantool.msgpack_ext.packer import default as packer_default + def packer_factory(conn): """ Build packer to pack request. @@ -225,6 +226,7 @@ def __init__(self, conn, space_no, values): self._body = request_body + def sha1(values): """ Compute hash. @@ -246,6 +248,7 @@ def sha1(values): sha.update(i.encode()) return sha.digest() + class RequestAuthenticate(Request): """ Represents AUTHENTICATE request. @@ -696,6 +699,7 @@ def __init__(self, conn, sql, args): self._body = request_body self.response_class = ResponseExecute + class RequestProtocolVersion(Request): """ Represents ID request: inform the server about the protocol diff --git a/tarantool/response.py b/tarantool/response.py index ac253df2..acd0cd9f 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -33,6 +33,7 @@ from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook + def unpacker_factory(conn): """ Build unpacker to unpack request response. @@ -67,7 +68,7 @@ def unpacker_factory(conn): # encoding option is not supported since msgpack-1.0.0, # but it is handled in the Connection constructor. - assert(msgpack.version < (1, 0, 0) or conn.encoding in (None, 'utf-8')) + assert msgpack.version < (1, 0, 0) or conn.encoding in (None, 'utf-8') # strict_map_key=True is default since msgpack-1.0.0. # @@ -130,8 +131,8 @@ def __init__(self, conn, response): self._return_code = 0 self._schema_version = header.get(IPROTO_SCHEMA_ID, None) self._data = self._body.get(IPROTO_DATA, None) - if (not isinstance(self._data, (list, tuple)) and - self._data is not None): + if (not isinstance(self._data, (list, tuple)) + and self._data is not None): self._data = [self._data] # # Backward-compatibility # if isinstance(self._data, (list, tuple)): @@ -303,7 +304,7 @@ def __str__(self): 'code': self.strerror[0], 'reason': self.return_message } - }, sort_keys = True, indent = 4, separators=(', ', ': ')) + }, sort_keys=True, indent=4, separators=(', ', ': ')) output = [] for tpl in self._data or (): output.extend(("- ", repr(tpl), "\n")) diff --git a/tarantool/schema.py b/tarantool/schema.py index 4cee7cdd..556737b7 100644 --- a/tarantool/schema.py +++ b/tarantool/schema.py @@ -24,6 +24,7 @@ Tarantool 2.10), but there are no restrictions in protocol. """ + def to_unicode(string): """ Decode :obj:`bytes` to unicode :obj:`str`. @@ -170,7 +171,7 @@ def __init__(self, space_row, schema): for part_id, part in enumerate(format_raw): part['id'] = part_id self.format[part['name']] = part - self.format[part_id ] = part + self.format[part_id] = part def flush(self): """ diff --git a/tarantool/types.py b/tarantool/types.py index d15262ac..d849dcfa 100644 --- a/tarantool/types.py +++ b/tarantool/types.py @@ -5,6 +5,7 @@ import typing from dataclasses import dataclass + @dataclass class BoxError(): """ @@ -77,6 +78,7 @@ class BoxError(): MP_ERROR_ERRCODE = 0x05 MP_ERROR_FIELDS = 0x06 + def decode_box_error(err_map): """ Decode MessagePack map received from Tarantool to `box.error`_ @@ -101,13 +103,14 @@ def decode_box_error(err_map): message=item[MP_ERROR_MESSAGE], errno=item[MP_ERROR_ERRNO], errcode=item[MP_ERROR_ERRCODE], - fields=item.get(MP_ERROR_FIELDS), # omitted if empty + fields=item.get(MP_ERROR_FIELDS), # omitted if empty prev=prev, ) prev = err return prev + def encode_box_error(err): """ Encode Python `box.error`_ representation to MessagePack map. @@ -132,7 +135,7 @@ def encode_box_error(err): MP_ERROR_ERRCODE: err.errcode, } - if err.fields is not None: # omitted if empty + if err.fields is not None: # omitted if empty dict_item[MP_ERROR_FIELDS] = err.fields stack.append(dict_item) diff --git a/tarantool/utils.py b/tarantool/utils.py index 296717f8..c7d9e246 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -9,6 +9,7 @@ ENCODING_DEFAULT = "utf-8" + def strxor(rhs, lhs): """ XOR two strings. @@ -24,6 +25,7 @@ def strxor(rhs, lhs): return bytes([x ^ y for x, y in zip(rhs, lhs)]) + def wrap_key(*args, first=True, select=False): """ Wrap request key in list, if needed. @@ -69,6 +71,7 @@ def version_id(major, minor, patch): return (((major << 8) | minor) << 8) | patch + @dataclass class Greeting(): """ @@ -95,6 +98,7 @@ class Greeting(): :type: :obj:`str`, optional """ + def greeting_decode(greeting_buf): """ Decode Tarantool server greeting. diff --git a/test/setup_command.py b/test/setup_command.py index 517c9109..64c285af 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -13,6 +13,7 @@ # pylint: disable=deprecated-module from distutils.errors import DistutilsError as BaseError + class Test(setuptools.Command): """ Class implementing `python setup.py test`. diff --git a/test/suites/__init__.py b/test/suites/__init__.py index 09e84821..bf07c9c4 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -35,6 +35,7 @@ TestSuiteInterval, TestSuiteErrorExt, TestSuitePush, TestSuiteConnection, TestSuiteCrud,) + def load_tests(loader, tests, pattern): """ Add suites to test run. @@ -46,10 +47,11 @@ def load_tests(loader, tests, pattern): suite.addTests(loader.loadTestsFromTestCase(testc)) return suite + __tmp = os.getcwd() os.chdir(os.path.abspath(os.path.dirname(__file__))) os.chdir(__tmp) # Workaround to disable unittest output truncating -__import__('sys').modules['unittest.util']._MAX_LENGTH = 99999 # pylint: disable=protected-access +__import__('sys').modules['unittest.util']._MAX_LENGTH = 99999 # pylint: disable=protected-access diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 15a82434..47765371 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -7,6 +7,7 @@ import pkg_resources + def fetch_tarantool_version(self): """ Helper to fetch current Tarantool version. @@ -27,6 +28,7 @@ def fetch_tarantool_version(self): except AttributeError: self.__class__.tnt_version = srv.admin.tnt_version + def skip_or_run_test_tarantool_impl(self, required_tt_version, msg): """ Helper to skip or run tests depending on the Tarantool @@ -64,6 +66,7 @@ def wrapper(self, *args, **kwargs): return wrapper + def skip_or_run_test_tarantool_call(self, required_tt_version, msg): """ Function to skip or run tests depending on the tarantool @@ -173,7 +176,8 @@ def skip_or_run_decimal_test(func): """ return skip_or_run_test_pcall_require(func, 'decimal', - 'does not support decimal type') + 'does not support decimal type') + def skip_or_run_uuid_test(func): """ @@ -187,6 +191,7 @@ def skip_or_run_uuid_test(func): return skip_or_run_test_tarantool(func, '2.4.1', 'does not support UUID type') + def skip_or_run_datetime_test(func): """ Decorator to skip or run datetime-related tests depending on @@ -197,7 +202,8 @@ def skip_or_run_datetime_test(func): """ return skip_or_run_test_pcall_require(func, 'datetime', - 'does not support datetime type') + 'does not support datetime type') + def skip_or_run_error_extra_info_test(func): """ @@ -211,6 +217,7 @@ def skip_or_run_error_extra_info_test(func): return skip_or_run_test_tarantool(func, '2.4.1', 'does not provide extra error info') + def skip_or_run_error_ext_type_test(func): """ Decorator to skip or run tests related to error extension @@ -225,6 +232,7 @@ def skip_or_run_error_ext_type_test(func): return skip_or_run_test_tarantool(func, '2.10.0', 'does not support error extension type') + def skip_or_run_ssl_password_test_call(self): """ Function to skip or run tests related to SSL password @@ -240,6 +248,7 @@ def skip_or_run_ssl_password_test_call(self): return skip_or_run_test_tarantool_call(self, '2.11.0', 'does not support SSL passwords') + def skip_or_run_auth_type_test_call(self): """ Function to skip or run tests related to configuring @@ -256,6 +265,7 @@ def skip_or_run_auth_type_test_call(self): return skip_or_run_test_tarantool_call(self, '2.11.0', 'does not support auth type') + def skip_or_run_constraints_test(func): """ Decorator to skip or run tests related to spaces with diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index eacf5323..46f3fff9 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -21,6 +21,7 @@ from .tarantool_admin import TarantoolAdmin from .remote_tarantool_server import RemoteTarantoolServer + def check_port(port, rais=True): """ Check if port is free. @@ -58,14 +59,14 @@ class TarantoolServer(): # pylint: disable=too-many-instance-attributes,too-many-arguments,duplicate-code default_tarantool = { - "bin": "tarantool", - "logfile": "tarantool.log", - "init": "init.lua"} + "bin": "tarantool", + "logfile": "tarantool.log", + "init": "init.lua"} default_cfg = { - "custom_proc_title": "\"tarantool-python testing\"", - "memtx_memory": 0.5 * 1024**3, # 0.5 GiB - "pid_file": "\"box.pid\""} + "custom_proc_title": "\"tarantool-python testing\"", + "memtx_memory": 0.5 * 1024**3, # 0.5 GiB + "pid_file": "\"box.pid\""} @property def logfile_path(self): @@ -343,9 +344,9 @@ def start(self): os.chmod(self.script_dst, 0o777) args = self.prepare_args() self.process = subprocess.Popen(args, - cwd=self.vardir, - stdout=self.log_des, - stderr=self.log_des) + cwd=self.vardir, + stdout=self.log_des, + stderr=self.log_des) self.wait_until_started() def stop(self): diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 52b3ebf0..8c7df685 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -15,6 +15,7 @@ from .lib.skip import skip_or_run_decimal_test, skip_or_run_varbinary_test from .lib.tarantool_server import TarantoolServer + class TestSuiteConnection(unittest.TestCase): @classmethod def setUpClass(cls): @@ -105,7 +106,6 @@ def my_unpacker_factory(_): return msgpack.Unpacker(ext_hook=my_ext_type_decoder, strict_map_key=False) return msgpack.Unpacker(ext_hook=my_ext_type_decoder) - self.con = tarantool.Connection(self.srv.host, self.srv.args['primary'], user='test', password='test', unpacker_factory=my_unpacker_factory) diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 67b13190..5c560082 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -13,6 +13,7 @@ from .lib.tarantool_server import TarantoolServer + def create_server(): srv = TarantoolServer() srv.script = 'test/suites/crud_server.lua' @@ -22,7 +23,7 @@ def create_server(): @unittest.skipIf(sys.platform.startswith("win"), - "Crud tests on windows platform are not supported: " + + "Crud tests on windows platform are not supported: " "complexity of the vshard replicaset configuration") class TestSuiteCrud(unittest.TestCase): @@ -41,17 +42,17 @@ def setUp(self): self.conn = tarantool.Connection(host=self.host, port=self.port, user='guest', password='', fetch_schema=False) self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port, - user='guest', password='', fetch_schema=False) - self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], - user='guest', password='', - fetch_schema=False) + user='guest', password='', fetch_schema=False) + self.conn_pool = tarantool.ConnectionPool([{'host': self.host, 'port': self.port}], + user='guest', password='', + fetch_schema=False) # Time for vshard group configuration. time.sleep(1) if self.conn.eval('return ROCKS_IMPORT_FAIL').data[0] is 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 ' + + 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 = { @@ -69,9 +70,7 @@ def setUp(self): 'args': ['tester', [1, 100, 'Bob'], {'timeout': 10}], }, 'output': { - 'str': [ - r'Duplicate key exists', - ], + 'str': [r'Duplicate key exists'], }, }, }, @@ -90,9 +89,7 @@ def setUp(self): {'timeout': 10}], }, 'output': { - 'str': [ - r'Duplicate key exists', - ], + 'str': [r'Duplicate key exists'], }, }, }, @@ -112,11 +109,11 @@ def setUp(self): }, 'output': { 'rows': [ - [3, 100, 'Jacob'], - [4, 100, 'Wyatt'], - [5, 100, 'David'], - [6, 100, 'Leo'], - ], + [3, 100, 'Jacob'], + [4, 100, 'Wyatt'], + [5, 100, 'David'], + [6, 100, 'Leo'], + ], }, }, 'error': { @@ -133,9 +130,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'Duplicate key exists', - ], + 'str': [r'Duplicate key exists'], 'res_rows': [[7, 100, 'Grayson'], [8, 100, 'Ezra']] }, }, @@ -156,11 +151,11 @@ def setUp(self): }, 'output': { 'rows': [ - [9, 100, 'Sharar'], - [10, 100, 'Thaddeus'], - [11, 100, 'Tobit'], - [12, 100, 'Zeb'], - ], + [9, 100, 'Sharar'], + [10, 100, 'Thaddeus'], + [11, 100, 'Tobit'], + [12, 100, 'Zeb'], + ], }, }, 'error': { @@ -177,9 +172,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'Duplicate key exists', - ], + 'str': [r'Duplicate key exists'], 'res_rows': [[13, 100, 'Uzzi'], [14, 100, 'Zimiri']] }, }, @@ -198,9 +191,7 @@ def setUp(self): 'args': ['no-such-space-name', [1, 100, 'Bob'], {'timeout': 10}], }, 'output': { - 'str': [ - r'GetError: Space "no-such-space-name" doesn\'t exist', - ], + 'str': [r'GetError: Space "no-such-space-name" doesn\'t exist'], }, }, }, @@ -218,9 +209,7 @@ def setUp(self): 'args': ['tester', 1, [['+', 'age', 1]], {'timeout': 10}], }, 'output': { - 'str': [ - r"UpdateError", - ], + 'str': [r"UpdateError"], }, }, }, @@ -238,9 +227,7 @@ def setUp(self): 'args': ['no-such-space-name', 1, {'timeout': 10}], }, 'output': { - 'str': [ - r'DeleteError: Space "no-such-space-name" doesn\'t exist', - ], + 'str': [r'DeleteError: Space "no-such-space-name" doesn\'t exist'], }, }, }, @@ -258,9 +245,7 @@ def setUp(self): 'args': ['tester', [1, 100, 0], {'timeout': 10}], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], }, }, }, @@ -281,9 +266,7 @@ def setUp(self): 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 0}, {'timeout': 10}], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], }, }, }, @@ -303,11 +286,11 @@ def setUp(self): }, 'output': { 'rows': [ - [2, 100, 'Cephus'], - [3, 100, 'Esau'], - [4, 100, 'Haman'], - [5, 100, 'Gershon'], - ], + [2, 100, 'Cephus'], + [3, 100, 'Esau'], + [4, 100, 'Haman'], + [5, 100, 'Gershon'], + ], }, }, 'error': { @@ -324,9 +307,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], 'res_rows': [[3, 100, 'Ephron'], [4, 100, 'Ethan']] }, }, @@ -347,11 +328,11 @@ def setUp(self): }, 'output': { 'rows': [ - [2, 100, 'Cephus'], - [3, 100, 'Esau'], - [4, 100, 'Haman'], - [5, 100, 'Gershon'], - ], + [2, 100, 'Cephus'], + [3, 100, 'Esau'], + [4, 100, 'Haman'], + [5, 100, 'Gershon'], + ], }, }, 'error': { @@ -368,9 +349,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], 'res_rows': [[3, 100, 'Ephron'], [4, 100, 'Ethan']] }, }, @@ -390,9 +369,7 @@ def setUp(self): 'args': ['tester', [2, 100, 'Cephus'], [['+', 'age', 1]], {'timeout': 10}], }, 'output': { - 'str': [ - r"UpsertError", - ], + 'str': [r"UpsertError"], }, }, }, @@ -400,7 +377,7 @@ def setUp(self): 'success': { 'input': { 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, - [['+', 'bucket_id', 1]], {'timeout': 10}], + [['+', 'bucket_id', 1]], {'timeout': 10}], }, 'output': { 'rows': [], @@ -409,12 +386,10 @@ def setUp(self): 'error': { 'input': { 'args': ['tester', {'id': 2, 'bucket_id': 100, 'name': 'Cephus'}, - [['+', 'age', 1]], {'timeout': 10}], + [['+', 'age', 1]], {'timeout': 10}], }, 'output': { - 'str': [ - r"UpsertError", - ], + 'str': [r"UpsertError"], }, }, }, @@ -448,9 +423,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], }, }, }, @@ -508,9 +481,7 @@ def setUp(self): ], }, 'output': { - 'str': [ - r'expected string', - ], + 'str': [r'expected string'], }, }, }, @@ -528,9 +499,7 @@ def setUp(self): 'args': ['no-such-space-name'], }, 'output': { - 'str': [ - r'SelectError: Space "no-such-space-name" doesn\'t exist', - ], + 'str': [r'SelectError: Space "no-such-space-name" doesn\'t exist'], }, }, }, @@ -548,9 +517,7 @@ def setUp(self): 'args': ['tester', 'no-idx'], }, 'output': { - 'str': [ - r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', - ], + 'str': [r'BorderError: Index "no-idx" of space "tester" doesn\'t exist'], }, }, }, @@ -568,9 +535,7 @@ def setUp(self): 'args': ['tester', 'no-idx', {'timeout': 10}], }, 'output': { - 'str': [ - r'BorderError: Index "no-idx" of space "tester" doesn\'t exist', - ], + 'str': [r'BorderError: Index "no-idx" of space "tester" doesn\'t exist'], }, }, }, @@ -588,9 +553,7 @@ def setUp(self): 'args': ['no-such-space-name', {'timeout': 10}], }, 'output': { - 'str': [ - r'LenError: Space "no-such-space-name" doesn\'t exist', - ], + 'str': [r'LenError: Space "no-such-space-name" doesn\'t exist'], }, }, }, @@ -608,9 +571,7 @@ def setUp(self): 'args': ['no-such-space-name', [['==', 'bucket_id', 100]], {'timeout': 10}], }, 'output': { - 'str': [ - r'CountError: Space "no-such-space-name" doesn\'t exist', - ], + 'str': [r'CountError: Space "no-such-space-name" doesn\'t exist'], }, }, }, @@ -646,7 +607,7 @@ def setUp(self): }, 'error': { 'input': { - 'args': [[],[]], + 'args': [[], []], }, 'output': { 'str': [], @@ -667,9 +628,7 @@ def setUp(self): 'args': ['no-such-space-name', {'timeout': 10}], }, 'output': { - 'str': [ - r'"no-such-space-name" doesn\'t exist', - ], + 'str': [r'"no-such-space-name" doesn\'t exist'], }, }, }, @@ -680,13 +639,13 @@ def setUp(self): }, 'output': { 'operations': [ - 'insert', 'replace', - 'upsert', 'len', - 'delete', 'get', - 'select', 'borders', - 'update', 'count', - 'truncate', - ], + 'insert', 'replace', + 'upsert', 'len', + 'delete', 'get', + 'select', 'borders', + 'update', 'count', + 'truncate', + ], }, }, 'error': { @@ -719,9 +678,9 @@ def _correct_operation_with_crud(self, testing_function, case, mode=None): if 'operations' in case['success']['output']: # Case for statistics testing. for operation in case['success']['output']['operations']: - self.assertEqual(operation in resp.__dict__, True, - 'Problem with finding a field with a statistic about operation ' - + operation) + self.assertEqual( + operation in resp.__dict__, True, + 'Problem with finding a field with a statistic about operation ' + operation) def _exception_operation_with_crud(self, testing_function, case, mode=None): try: diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index c4278120..f02cf523 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -17,6 +17,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test + class TestSuiteDatetime(unittest.TestCase): @classmethod def setUpClass(cls): @@ -68,7 +69,6 @@ def setUp(self): self.adm("box.space['test']:truncate()") - def test_datetime_class_api(self): datetime = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tzoffset=180) @@ -103,7 +103,6 @@ def test_datetime_class_api_wth_tz(self): self.assertEqual(datetime.tz, 'Europe/Moscow') self.assertEqual(datetime.value, 1661958474308543321) - datetime_class_invalid_init_cases = { 'positional_year': { 'args': [2022], @@ -152,7 +151,6 @@ def test_datetime_class_invalid_init(self): case['type'], re.escape(case['msg']), lambda: tarantool.Datetime(*case['args'], **case['kwargs'])) - integration_cases = { 'date': { 'python': tarantool.Datetime(year=2022, month=8, day=31), @@ -183,14 +181,14 @@ def test_datetime_class_invalid_init(self): 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543000), 'msgpack': (b'\x7a\xa3\x0f\x63\x00\x00\x00\x00\x18\xfe\x63\x12\x00\x00\x00\x00'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543000})", }, 'datetime_with_nanoseconds': { 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321), 'msgpack': (b'\x7a\xa3\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\x00\x00\x00\x00'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321})", }, 'date_before_1970_with_nanoseconds': { @@ -212,14 +210,14 @@ def test_datetime_class_invalid_init(self): 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tzoffset=180), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\x00\x00'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tzoffset=180})", }, 'datetime_with_negative_offset': { 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tzoffset=-60), 'msgpack': (b'\x8a\xb1\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xc4\xff\x00\x00'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tzoffset=-60})", }, 'timestamp_with_positive_offset': { @@ -246,7 +244,7 @@ def test_datetime_class_invalid_init(self): 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tz='Europe/Moscow'), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\xb3\x03'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tz='Europe/Moscow'})", }, 'datetime_with_tz_winter_time': { @@ -258,28 +256,28 @@ def test_datetime_class_invalid_init(self): 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tz='Europe/Moscow', tzoffset=123), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\xb3\x03'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tz='Europe/Moscow', tzoffset=123})", }, 'datetime_with_abbrev_tz': { 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tz='MSK'), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\xee\x00'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tz='MSK'})", }, 'datetime_with_abbrev_tz_and_zero_offset': { 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, nsec=308543321, tz='AZODT'), 'msgpack': (b'\x7a\xa3\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\x00\x00\x12\x02'), - 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " + + 'tarantool': r"datetime.new({year=2022, month=8, day=31, hour=18, min=7, sec=54, " r"nsec=308543321, tz='AZODT'})", }, 'timestamp_since_utc_epoch': { 'python': tarantool.Datetime(timestamp=1661958474, nsec=308543321, tz='Europe/Moscow', timestamp_since_utc_epoch=True), 'msgpack': (b'\x4a\x79\x0f\x63\x00\x00\x00\x00\x59\xff\x63\x12\xb4\x00\xb3\x03'), - 'tarantool': r"datetime.new({timestamp=1661969274, nsec=308543321, " + + 'tarantool': r"datetime.new({timestamp=1661969274, nsec=308543321, " r"tz='Europe/Moscow'})", }, } @@ -313,7 +311,7 @@ def test_tarantool_encode(self): lua_eval = f""" local dt = {case['tarantool']} - + local tuple = box.space['test']:get('{name}') assert(tuple ~= nil) @@ -339,7 +337,6 @@ def test_msgpack_decode_ambiguous_tzindex(self): ValueError, 'Failed to create datetime with ambiguous timezone "AET"', lambda: unpacker_ext_hook(4, case)) - datetime_subtraction_cases = { 'date': { 'arg_1': tarantool.Datetime(year=2008, month=2, day=3), @@ -379,7 +376,6 @@ def test_tarantool_datetime_subtraction(self): self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res']]) - datetime_subtraction_different_timezones_case = { 'arg_1': tarantool.Datetime(year=2001, month=2, day=3, tz='UTC'), 'arg_2': tarantool.Datetime(year=2001, month=2, day=3, tz='MSK'), @@ -397,8 +393,7 @@ def test_tarantool_datetime_subtraction_different_timezones(self): case = self.datetime_subtraction_different_timezones_case self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), - [case['res']]) - + [case['res']]) interval_arithmetic_cases = { 'year': { @@ -435,9 +430,9 @@ def test_tarantool_datetime_subtraction_different_timezones(self): 'arg_1': tarantool.Datetime(year=2008, month=2, day=3, hour=3, minute=36, sec=43), 'arg_2': tarantool.Interval(nsec=10000023), 'res_add': tarantool.Datetime(year=2008, month=2, day=3, hour=3, minute=36, sec=43, - nsec=10000023), + nsec=10000023), 'res_sub': tarantool.Datetime(year=2008, month=2, day=3, hour=3, minute=36, sec=42, - nsec=989999977), + nsec=989999977), }, 'zero': { 'arg_1': tarantool.Datetime(year=2008, month=2, day=3, hour=3, minute=36, sec=43), @@ -507,7 +502,6 @@ def test_tarantool_interval_subtraction(self): self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res_sub']]) - datetime_addition_winter_time_switch_case = { 'arg_1': tarantool.Datetime(year=2008, month=1, day=1, hour=12, tz='Europe/Moscow'), 'arg_2': tarantool.Interval(month=6), @@ -527,7 +521,6 @@ def test_tarantool_datetime_addition_winter_time_switch(self): self.assertSequenceEqual(self.con.call('add', case['arg_1'], case['arg_2']), [case['res']]) - @skip_or_run_datetime_test def test_primary_key(self): data = [tarantool.Datetime(year=1970, month=1, day=1), 'content'] diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 5968f648..65799960 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -67,25 +67,29 @@ def test_rowcount(self): try: cur = con.cursor() self.executeDDL1(cur) - dbapi20._failUnless(self,cur.rowcount in (-1, 1), + dbapi20._failUnless( + self, cur.rowcount in (-1, 1), 'cursor.rowcount should be -1 or 1 after executing no-result ' 'statements' + str(cur.rowcount) - ) + ) cur.execute(f"{self.insert} into {self.table_prefix}booze values ('Victoria Bitter')") - dbapi20._failUnless(self,cur.rowcount == 1, + dbapi20._failUnless( + self, cur.rowcount == 1, 'cursor.rowcount should == number or rows inserted, or ' 'set to -1 after executing an insert statement' - ) + ) cur.execute(f"select name from {self.table_prefix}booze") - dbapi20._failUnless(self,cur.rowcount == -1, + dbapi20._failUnless( + self, cur.rowcount == -1, 'cursor.rowcount should == number of rows returned, or ' 'set to -1 after executing a select statement' - ) + ) self.executeDDL2(cur) - dbapi20._failUnless(self,cur.rowcount in (-1, 1), + dbapi20._failUnless( + self, cur.rowcount in (-1, 1), 'cursor.rowcount should be -1 or 1 after executing no-result ' 'statements' - ) + ) finally: con.close() diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index 9d7d8211..c817ad08 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -17,6 +17,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_decimal_test + class TestSuiteDecimal(unittest.TestCase): @classmethod def setUpClass(cls): @@ -58,7 +59,6 @@ def setUp(self): self.adm("box.space['test']:truncate()") - valid_cases = { 'simple_decimal_1': { 'python': decimal.Decimal('0.7'), @@ -162,13 +162,13 @@ def setUp(self): }, 'decimal_limits_1': { 'python': decimal.Decimal('11111111111111111111111111111111111111'), - 'msgpack': (b'\x00\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11' + + 'msgpack': (b'\x00\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11' b'\x11\x11\x11\x11\x11\x11\x11\x11\x11\x1c'), 'tarantool': "decimal.new('11111111111111111111111111111111111111')", }, 'decimal_limits_2': { 'python': decimal.Decimal('-11111111111111111111111111111111111111'), - 'msgpack': (b'\x00\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11' + + 'msgpack': (b'\x00\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11' b'\x11\x11\x11\x11\x11\x11\x11\x11\x11\x1d'), 'tarantool': "decimal.new('-11111111111111111111111111111111111111')", }, @@ -204,25 +204,25 @@ def setUp(self): }, 'decimal_limits_9': { 'python': decimal.Decimal('99999999999999999999999999999999999999'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9c'), 'tarantool': "decimal.new('99999999999999999999999999999999999999')", }, 'decimal_limits_10': { 'python': decimal.Decimal('-99999999999999999999999999999999999999'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9d'), 'tarantool': "decimal.new('-99999999999999999999999999999999999999')", }, 'decimal_limits_11': { 'python': decimal.Decimal('1234567891234567890.0987654321987654321'), - 'msgpack': (b'\x13\x01\x23\x45\x67\x89\x12\x34\x56\x78\x90' + + 'msgpack': (b'\x13\x01\x23\x45\x67\x89\x12\x34\x56\x78\x90' b'\x09\x87\x65\x43\x21\x98\x76\x54\x32\x1c'), 'tarantool': "decimal.new('1234567891234567890.0987654321987654321')", }, 'decimal_limits_12': { 'python': decimal.Decimal('-1234567891234567890.0987654321987654321'), - 'msgpack': (b'\x13\x01\x23\x45\x67\x89\x12\x34\x56\x78\x90' + + 'msgpack': (b'\x13\x01\x23\x45\x67\x89\x12\x34\x56\x78\x90' b'\x09\x87\x65\x43\x21\x98\x76\x54\x32\x1d'), 'tarantool': "decimal.new('-1234567891234567890.0987654321987654321')", }, @@ -271,7 +271,6 @@ def test_tarantool_encode(self): self.assertSequenceEqual(self.con.eval(lua_eval), [True]) - error_cases = { 'decimal_limit_break_head_1': { 'python': decimal.Decimal('999999999999999999999999999999999999999'), @@ -322,7 +321,6 @@ def test_tarantool_encode_error(self): MsgpackError, msg, lambda: self.con.insert('test', [name, case['python']])) - precision_loss_cases = { 'decimal_limit_break_tail_1': { 'python': decimal.Decimal('1.00000000000000000000000000000000000001'), @@ -356,27 +354,27 @@ def test_tarantool_encode_error(self): }, 'decimal_limit_break_tail_7': { 'python': decimal.Decimal('99999999999999999999999999999999999999.1'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9c'), 'tarantool': "decimal.new('99999999999999999999999999999999999999')", }, 'decimal_limit_break_tail_8': { 'python': decimal.Decimal('-99999999999999999999999999999999999999.1'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9d'), 'tarantool': "decimal.new('-99999999999999999999999999999999999999')", }, 'decimal_limit_break_tail_9': { 'python': decimal.Decimal('99999999999999999999999999999999999999.11111111111111' '11111111111'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9c'), 'tarantool': "decimal.new('99999999999999999999999999999999999999')", }, 'decimal_limit_break_tail_10': { 'python': decimal.Decimal('-99999999999999999999999999999999999999.11111111111111' '11111111111'), - 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' + + 'msgpack': (b'\x00\x09\x99\x99\x99\x99\x99\x99\x99\x99\x99' b'\x99\x99\x99\x99\x99\x99\x99\x99\x99\x9d'), 'tarantool': "decimal.new('-99999999999999999999999999999999999999')", }, @@ -393,11 +391,10 @@ def test_msgpack_encode_with_precision_loss(self): self.assertWarnsRegex( MsgpackWarning, msg, lambda: self.assertEqual( - packer_default(case['python']), - msgpack.ExtType(code=1, data=case['msgpack']) - ) + packer_default(case['python']), + msgpack.ExtType(code=1, data=case['msgpack']) ) - + ) @skip_or_run_decimal_test def test_tarantool_encode_with_precision_loss(self): @@ -427,7 +424,6 @@ def test_tarantool_encode_with_precision_loss(self): self.assertSequenceEqual(self.con.eval(lua_eval), [True]) - @skip_or_run_decimal_test def test_primary_key(self): data = [decimal.Decimal('0'), 'content'] @@ -435,7 +431,6 @@ def test_primary_key(self): self.assertSequenceEqual(self.con.insert('test_pk', data), [data]) self.assertSequenceEqual(self.con.select('test_pk', data[0]), [data]) - @classmethod def tearDownClass(cls): cls.con.close() diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index bf0d8f03..0263d451 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -11,6 +11,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_extra_info_test + class TestSuiteRequest(unittest.TestCase): @classmethod def setUpClass(cls): @@ -77,9 +78,10 @@ def test_00_02_fill_space(self): # prevent a remote tarantool from clean our session self.srv.touch_lock() self.assertEqual( - self.con.insert('space_1', [i, i%5, 'tuple_'+str(i)])[0], - [i, i%5, 'tuple_'+str(i)] + self.con.insert('space_1', [i, i % 5, 'tuple_' + str(i)])[0], + [i, i % 5, 'tuple_' + str(i)] ) + def test_00_03_answer_repr(self): repr_str = """- [1, 1, 'tuple_1']""" self.assertEqual(repr(self.con.select('space_1', 1)), repr_str) @@ -97,24 +99,23 @@ def test_02_select(self): [[20, 0, 'tuple_20']]) # With other indexes too self.assertSequenceEqual( - sorted( - self.con.select('space_1', [0], index='secondary', offset=3, limit=0), - key = lambda x: x[0]), - [] - ) + sorted( + self.con.select('space_1', [0], index='secondary', offset=3, limit=0), + key=lambda x: x[0]), + [] + ) self.assertSequenceEqual( - sorted( - self.con.select('space_1', [0], index='secondary', offset=3, limit=1), - key = lambda x: x[0]), - [[110, 0, 'tuple_110']] - ) + sorted( + self.con.select('space_1', [0], index='secondary', offset=3, limit=1), + key=lambda x: x[0]), + [[110, 0, 'tuple_110']] + ) self.assertSequenceEqual( - sorted( - self.con.select('space_1', [0], index='secondary', offset=3, limit=2), - key = lambda x: x[0]), - [[110, 0, 'tuple_110'],\ - [115, 0, 'tuple_115']] - ) + sorted( + self.con.select('space_1', [0], index='secondary', offset=3, limit=2), + key=lambda x: x[0]), + [[110, 0, 'tuple_110'], [115, 0, 'tuple_115']] + ) select_req = self.con.select('space_1', [0], index='secondary') self.assertEqual(len(select_req), 99) @@ -134,7 +135,7 @@ def test_02_select(self): # TODO: Check iterator_types self.assertSequenceEqual( self.con.select('space_1', [0, 'tuple_20'], index='secondary', limit=2, - iterator=tarantool.const.ITERATOR_GT), + iterator=tarantool.const.ITERATOR_GT), [[200, 0, 'tuple_200'], [205, 0, 'tuple_205']] ) @@ -144,20 +145,18 @@ def test_03_delete(self): self.assertSequenceEqual(self.con.delete('space_1', [20]), []) self.assertSequenceEqual(self.con.select('space_1', [20], index='primary'), []) # Check that field has no meaning, yet. - with self.assertRaisesRegex(tarantool.DatabaseError, - '(19, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(19, .*)'): self.con.delete('space_1', [1, 'tuple_21']) self.assertSequenceEqual(self.con.select('space_1', [21], index='primary'), - [[21, 1, 'tuple_21']]) + [[21, 1, 'tuple_21']]) def test_04_replace(self): # Check replace that is Ok. self.assertSequenceEqual(self.con.replace('space_1', [2, 2, 'tuple_3']), - [[2, 2, 'tuple_3']]) + [[2, 2, 'tuple_3']]) self.assertSequenceEqual(self.con.select('space_1', 2), [[2, 2, 'tuple_3']]) # Check replace that isn't Ok. - with self.assertRaisesRegex(tarantool.DatabaseError, - '(39, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(39, .*)'): self.assertSequenceEqual(self.con.replace('space_1', [2, 2]), [[2, 2, 'tuple_2']]) def test_05_ping(self): @@ -172,28 +171,28 @@ def test_05_ping(self): def test_06_update(self): self.assertSequenceEqual(self.con.update('space_1', (2,), [('+', 1, 3)]), - [[2, 5, 'tuple_3']]) + [[2, 5, 'tuple_3']]) self.assertSequenceEqual(self.con.update('space_1', (2,), [('-', 1, 3)]), - [[2, 2, 'tuple_3']]) + [[2, 2, 'tuple_3']]) self.assertSequenceEqual(self.con.update('space_1', (2,), [(':', 2, 3, 2, 'lalal')]), - [[2, 2, 'tuplalal_3']]) + [[2, 2, 'tuplalal_3']]) self.assertSequenceEqual(self.con.update('space_1', (2,), [('!', 2, '1')]), - [[2, 2, '1', 'tuplalal_3']]) + [[2, 2, '1', 'tuplalal_3']]) self.assertSequenceEqual(self.con.update('space_1', (2,), [('!', 2, 'oingo, boingo')]), - [[2, 2, 'oingo, boingo', '1', 'tuplalal_3']]) + [[2, 2, 'oingo, boingo', '1', 'tuplalal_3']]) self.assertSequenceEqual(self.con.update('space_1', (2,), [('#', 2, 2)]), - [[2, 2, 'tuplalal_3']]) + [[2, 2, 'tuplalal_3']]) def test_07_call_16(self): - con = tarantool.Connection(self.srv.host, self.srv.args['primary'], call_16 = True) + con = tarantool.Connection(self.srv.host, self.srv.args['primary'], call_16=True) try: con.authenticate('test', 'test') self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), - [[123, 234, 345]]) + [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), - [[123, 234, 345]]) + [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), - [[123, 234, 345]]) + [[123, 234, 345]]) with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode') with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): @@ -212,7 +211,7 @@ def test_07_call_16(self): self.assertIsInstance(ans[0][0], str) self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), - [[1, 2, 3, 'fld_1']]) + [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) finally: con.close() @@ -238,41 +237,41 @@ def test_07_call_17(self): self.assertIsInstance(ans[0], str) self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), - [[1, 2, 3, 'fld_1']]) + [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) con.close() def test_08_eval(self): self.assertSequenceEqual(self.con.eval('return json.decode(...)', - '[123, 234, 345]'), [[123, 234, 345]]) + '[123, 234, 345]'), [[123, 234, 345]]) self.assertSequenceEqual(self.con.eval('return json.decode(...)', - ['[123, 234, 345]']), [[123, 234, 345]]) + ['[123, 234, 345]']), [[123, 234, 345]]) self.assertSequenceEqual(self.con.eval('return json.decode(...)', - ('[123, 234, 345]',)), [[123, 234, 345]]) + ('[123, 234, 345]',)), [[123, 234, 345]]) self.assertSequenceEqual(self.con.eval('return json.decode("[123, 234, 345]")'), - [[123, 234, 345]]) - self.assertSequenceEqual(self.con.eval('return json.decode("[123, 234, 345]"), '+ - 'json.decode("[123, 234, 345]")'), - [[123, 234, 345], [123, 234, 345]]) + [[123, 234, 345]]) + self.assertSequenceEqual( + self.con.eval('return json.decode("[123, 234, 345]"), json.decode("[123, 234, 345]")'), + [[123, 234, 345], [123, 234, 345]]) self.assertSequenceEqual(self.con.eval('json.decode("[123, 234, 345]")'), []) def test_09_upsert(self): self.assertSequenceEqual(self.con.select('space_1', [22], index='primary'), - [[22, 2, 'tuple_22']]) + [[22, 2, 'tuple_22']]) self.assertSequenceEqual(self.con.select('space_1', [23], index='primary'), - [[23, 3, 'tuple_23']]) + [[23, 3, 'tuple_23']]) self.assertSequenceEqual(self.con.select('space_1', [499], index='primary'), - [[499, 4, 'tuple_499']]) + [[499, 4, 'tuple_499']]) self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), []) - self.assertSequenceEqual(self.con.upsert('space_1', [500, 123, 'hello, world'], - [(':', 2, 2, 3, "---")]), []) + self.assertSequenceEqual( + self.con.upsert('space_1', [500, 123, 'hello, world'], [(':', 2, 2, 3, "---")]), []) self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), - [[500, 123, 'hello, world']]) + [[500, 123, 'hello, world']]) self.assertSequenceEqual(self.con.upsert('space_1', [500, 123, 'hello, world'], - [(':', 2, 2, 3, "---")]), []) + [(':', 2, 2, 3, "---")]), []) self.assertSequenceEqual(self.con.select('space_1', [500], index='primary'), - [[500, 123, 'he---, world']]) + [[500, 123, 'he---, world']]) def test_10_space(self): space = self.con.space('space_1') @@ -289,15 +288,15 @@ def test_10_space(self): [22, 10, 'lol'] ]) self.assertSequenceEqual(space.select([501], index='primary'), []) - self.assertSequenceEqual(space.upsert([501, 123, 'hello, world'], - [(':', 2, 2, 3, "---")]), []) + self.assertSequenceEqual( + space.upsert([501, 123, 'hello, world'], [(':', 2, 2, 3, "---")]), []) self.assertSequenceEqual(space.select([501], index='primary'), [[501, 123, 'hello, world']]) - self.assertSequenceEqual(space.upsert([501, 123, 'hello, world'], - [(':', 2, 2, 3, "---")]), []) + self.assertSequenceEqual( + space.upsert([501, 123, 'hello, world'], [(':', 2, 2, 3, "---")]), []) self.assertSequenceEqual(space.update([400], [('!', 2, 'oingo, boingo')]), - [[400, 0, 'oingo, boingo', 'tuple_400']]) + [[400, 0, 'oingo, boingo', 'tuple_400']]) self.assertSequenceEqual(space.update([400], [('#', 2, 1)]), - [[400, 0, 'tuple_400']]) + [[400, 0, 'tuple_400']]) self.assertSequenceEqual(space.delete([900]), [[900, 10, 'foo']]) def test_11_select_all_hash(self): @@ -313,20 +312,20 @@ def test_11_select_all_hash(self): def test_12_update_fields(self): self.srv.admin( - """ - do - local sp = box.schema.create_space('sp', { - format = { - { name = 'fir', type = 'unsigned' }, - { name = 'sec', type = 'string' }, - { name = 'thi', type = 'unsigned' }, - } - }) - sp:create_index('pr', { - parts = {1, 'unsigned'} - }) - end - """) + """ + do + local sp = box.schema.create_space('sp', { + format = { + { name = 'fir', type = 'unsigned' }, + { name = 'sec', type = 'string' }, + { name = 'thi', type = 'unsigned' }, + } + }) + sp:create_index('pr', { + parts = {1, 'unsigned'} + }) + end + """) self.con.insert('sp', [2, 'help', 4]) self.assertSequenceEqual( self.con.update('sp', (2,), [('+', 'thi', 3)]), @@ -406,8 +405,8 @@ def test_16_extra_error_info_fields(self): self.assertTrue(isinstance(exc.extra_info.file, str)) self.assertTrue(exc.extra_info.line > 0) self.assertEqual( - exc.extra_info.message, - "Create access to function 'forbidden_function' is denied for user 'test'") + exc.extra_info.message, + "Create access to function 'forbidden_function' is denied for user 'test'") self.assertEqual(exc.extra_info.errno, 0) self.assertEqual(exc.extra_info.errcode, 42) self.assertEqual( diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 7149b43c..cadd2102 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -12,6 +12,7 @@ from .lib.skip import skip_or_run_varbinary_test, skip_or_run_error_extra_info_test from .lib.tarantool_server import TarantoolServer + class TestSuiteEncoding(unittest.TestCase): # pylint: disable=invalid-name @@ -29,7 +30,7 @@ def setUpClass(cls): """) args = [cls.srv.host, cls.srv.args['primary']] - kwargs = { 'user': 'test', 'password': 'test' } + kwargs = {'user': 'test', 'password': 'test'} cls.con_encoding_utf8 = tarantool.Connection(*args, encoding='utf-8', **kwargs) cls.con_encoding_none = tarantool.Connection(*args, encoding=None, **kwargs) cls.conns = [cls.con_encoding_utf8, cls.con_encoding_none] @@ -73,7 +74,7 @@ def setUpClass(cls): def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except self.fail(f'Function raised Exception: {repr(exc)}') def setUp(self): @@ -112,7 +113,7 @@ def test_01_03_bytes_encode_for_encoding_utf8_behavior(self): self.assertNotRaises(self.con_encoding_utf8.insert, space, [data_id, data]) - resp = self.con_encoding_utf8.select(space, [ data ], index='varbin') + resp = self.con_encoding_utf8.select(space, [data], index='varbin') self.assertSequenceEqual(resp, [[data_id, data]]) @skip_or_run_varbinary_test diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 373ea1b8..8d1a63cf 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -16,6 +16,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_ext_type_test + class TestSuiteErrorExt(unittest.TestCase): @classmethod def setUpClass(cls): @@ -79,7 +80,6 @@ def setUp(self): self.adm("box.space['test']:truncate()") - # msgpack data for different encodings are actually the same, # but sometimes python msgpack module use different string # types (str8 and str16) for the same strings depending on use_bin_type: @@ -101,9 +101,9 @@ def setUp(self): errno=0, errcode=0, ), - 'msgpack': (b'\x81\x00\x91\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + - b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + - b'\x01\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + + 'msgpack': (b'\x81\x00\x91\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + b'\x01\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' b'\x72\x72\x6f\x72\x04\x00\x05\x00'), 'tarantool': "simple_error", }, @@ -118,9 +118,9 @@ def setUp(self): errno=0, errcode=0, ), - 'msgpack': (b'\x81\x00\x91\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + - b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + - b'\x01\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + + 'msgpack': (b'\x81\x00\x91\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + b'\x01\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' b'\x72\x72\x6f\x72\x04\x00\x05\x00'), 'tarantool': "simple_error", }, @@ -141,26 +141,26 @@ def setUp(self): 'access_type': 'Execute', }, ), - 'msgpack': (b'\x81\x00\x91\x87\x00\xb1\x41\x63\x63\x65\x73\x73' + - b'\x44\x65\x6e\x69\x65\x64\x45\x72\x72\x6f\x72\x01' + - b'\xd9\x34\x2f\x5f\x5f\x77\x2f\x73\x64\x6b\x2f\x73' + - b'\x64\x6b\x2f\x74\x61\x72\x61\x6e\x74\x6f\x6f\x6c' + - b'\x2d\x32\x2e\x31\x30\x2f\x74\x61\x72\x61\x6e\x74' + - b'\x6f\x6f\x6c\x2f\x73\x72\x63\x2f\x62\x6f\x78\x2f' + - b'\x66\x75\x6e\x63\x2e\x63\x02\xcd\x02\x17\x03\xd9' + - b'\x4e\x45\x78\x65\x63\x75\x74\x65\x20\x61\x63\x63' + - b'\x65\x73\x73\x20\x74\x6f\x20\x66\x75\x6e\x63\x74' + - b'\x69\x6f\x6e\x20\x27\x66\x6f\x72\x62\x69\x64\x64' + - b'\x65\x6e\x5f\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27' + - b'\x20\x69\x73\x20\x64\x65\x6e\x69\x65\x64\x20\x66' + - b'\x6f\x72\x20\x75\x73\x65\x72\x20\x27\x6e\x6f\x5f' + - b'\x67\x72\x61\x6e\x74\x73\x27\x04\x00\x05\x2a\x06' + - b'\x83\xab\x6f\x62\x6a\x65\x63\x74\x5f\x74\x79\x70' + - b'\x65\xa8\x66\x75\x6e\x63\x74\x69\x6f\x6e\xab\x6f' + - b'\x62\x6a\x65\x63\x74\x5f\x6e\x61\x6d\x65\xb2\x66' + - b'\x6f\x72\x62\x69\x64\x64\x65\x6e\x5f\x66\x75\x6e' + - b'\x63\x74\x69\x6f\x6e\xab\x61\x63\x63\x65\x73\x73' + - b'\x5f\x74\x79\x70\x65\xa7\x45\x78\x65\x63\x75\x74' + + 'msgpack': (b'\x81\x00\x91\x87\x00\xb1\x41\x63\x63\x65\x73\x73' + b'\x44\x65\x6e\x69\x65\x64\x45\x72\x72\x6f\x72\x01' + b'\xd9\x34\x2f\x5f\x5f\x77\x2f\x73\x64\x6b\x2f\x73' + b'\x64\x6b\x2f\x74\x61\x72\x61\x6e\x74\x6f\x6f\x6c' + b'\x2d\x32\x2e\x31\x30\x2f\x74\x61\x72\x61\x6e\x74' + b'\x6f\x6f\x6c\x2f\x73\x72\x63\x2f\x62\x6f\x78\x2f' + b'\x66\x75\x6e\x63\x2e\x63\x02\xcd\x02\x17\x03\xd9' + b'\x4e\x45\x78\x65\x63\x75\x74\x65\x20\x61\x63\x63' + b'\x65\x73\x73\x20\x74\x6f\x20\x66\x75\x6e\x63\x74' + b'\x69\x6f\x6e\x20\x27\x66\x6f\x72\x62\x69\x64\x64' + b'\x65\x6e\x5f\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27' + b'\x20\x69\x73\x20\x64\x65\x6e\x69\x65\x64\x20\x66' + b'\x6f\x72\x20\x75\x73\x65\x72\x20\x27\x6e\x6f\x5f' + b'\x67\x72\x61\x6e\x74\x73\x27\x04\x00\x05\x2a\x06' + b'\x83\xab\x6f\x62\x6a\x65\x63\x74\x5f\x74\x79\x70' + b'\x65\xa8\x66\x75\x6e\x63\x74\x69\x6f\x6e\xab\x6f' + b'\x62\x6a\x65\x63\x74\x5f\x6e\x61\x6d\x65\xb2\x66' + b'\x6f\x72\x62\x69\x64\x64\x65\x6e\x5f\x66\x75\x6e' + b'\x63\x74\x69\x6f\x6e\xab\x61\x63\x63\x65\x73\x73' + b'\x5f\x74\x79\x70\x65\xa7\x45\x78\x65\x63\x75\x74' b'\x65'), 'tarantool': "access_denied_error", 'ignore_file_info': True, @@ -182,26 +182,26 @@ def setUp(self): b'access_type': b'Execute', }, ), - 'msgpack': (b'\x81\x00\x91\x87\x00\xb1\x41\x63\x63\x65\x73\x73' + - b'\x44\x65\x6e\x69\x65\x64\x45\x72\x72\x6f\x72\x01' + - b'\xda\x00\x34\x2f\x5f\x5f\x77\x2f\x73\x64\x6b\x2f' + - b'\x73\x64\x6b\x2f\x74\x61\x72\x61\x6e\x74\x6f\x6f' + - b'\x6c\x2d\x32\x2e\x31\x30\x2f\x74\x61\x72\x61\x6e' + - b'\x74\x6f\x6f\x6c\x2f\x73\x72\x63\x2f\x62\x6f\x78' + - b'\x2f\x66\x75\x6e\x63\x2e\x63\x02\xcd\x02\x17\x03' + - b'\xda\x00\x4e\x45\x78\x65\x63\x75\x74\x65\x20\x61' + - b'\x63\x63\x65\x73\x73\x20\x74\x6f\x20\x66\x75\x6e' + - b'\x63\x74\x69\x6f\x6e\x20\x27\x66\x6f\x72\x62\x69' + - b'\x64\x64\x65\x6e\x5f\x66\x75\x6e\x63\x74\x69\x6f' + - b'\x6e\x27\x20\x69\x73\x20\x64\x65\x6e\x69\x65\x64' + - b'\x20\x66\x6f\x72\x20\x75\x73\x65\x72\x20\x27\x6e' + - b'\x6f\x5f\x67\x72\x61\x6e\x74\x73\x27\x04\x00\x05' + - b'\x2a\x06\x83\xab\x6f\x62\x6a\x65\x63\x74\x5f\x74' + - b'\x79\x70\x65\xa8\x66\x75\x6e\x63\x74\x69\x6f\x6e' + - b'\xab\x6f\x62\x6a\x65\x63\x74\x5f\x6e\x61\x6d\x65' + - b'\xb2\x66\x6f\x72\x62\x69\x64\x64\x65\x6e\x5f\x66' + - b'\x75\x6e\x63\x74\x69\x6f\x6e\xab\x61\x63\x63\x65' + - b'\x73\x73\x5f\x74\x79\x70\x65\xa7\x45\x78\x65\x63' + + 'msgpack': (b'\x81\x00\x91\x87\x00\xb1\x41\x63\x63\x65\x73\x73' + b'\x44\x65\x6e\x69\x65\x64\x45\x72\x72\x6f\x72\x01' + b'\xda\x00\x34\x2f\x5f\x5f\x77\x2f\x73\x64\x6b\x2f' + b'\x73\x64\x6b\x2f\x74\x61\x72\x61\x6e\x74\x6f\x6f' + b'\x6c\x2d\x32\x2e\x31\x30\x2f\x74\x61\x72\x61\x6e' + b'\x74\x6f\x6f\x6c\x2f\x73\x72\x63\x2f\x62\x6f\x78' + b'\x2f\x66\x75\x6e\x63\x2e\x63\x02\xcd\x02\x17\x03' + b'\xda\x00\x4e\x45\x78\x65\x63\x75\x74\x65\x20\x61' + b'\x63\x63\x65\x73\x73\x20\x74\x6f\x20\x66\x75\x6e' + b'\x63\x74\x69\x6f\x6e\x20\x27\x66\x6f\x72\x62\x69' + b'\x64\x64\x65\x6e\x5f\x66\x75\x6e\x63\x74\x69\x6f' + b'\x6e\x27\x20\x69\x73\x20\x64\x65\x6e\x69\x65\x64' + b'\x20\x66\x6f\x72\x20\x75\x73\x65\x72\x20\x27\x6e' + b'\x6f\x5f\x67\x72\x61\x6e\x74\x73\x27\x04\x00\x05' + b'\x2a\x06\x83\xab\x6f\x62\x6a\x65\x63\x74\x5f\x74' + b'\x79\x70\x65\xa8\x66\x75\x6e\x63\x74\x69\x6f\x6e' + b'\xab\x6f\x62\x6a\x65\x63\x74\x5f\x6e\x61\x6d\x65' + b'\xb2\x66\x6f\x72\x62\x69\x64\x64\x65\x6e\x5f\x66' + b'\x75\x6e\x63\x74\x69\x6f\x6e\xab\x61\x63\x63\x65' + b'\x73\x73\x5f\x74\x79\x70\x65\xa7\x45\x78\x65\x63' b'\x75\x74\x65'), 'tarantool': "access_denied_error", 'ignore_file_info': True, @@ -225,12 +225,12 @@ def setUp(self): errcode=0, ), ), - 'msgpack': (b'\x81\x00\x92\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + - b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + - b'\x03\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + - b'\x72\x72\x6f\x72\x04\x00\x05\x00\x86\x00\xab\x43' + - b'\x6c\x69\x65\x6e\x74\x45\x72\x72\x6f\x72\x01\xa4' + - b'\x65\x76\x61\x6c\x02\x02\x03\xad\x55\x6e\x6b\x6e' + + 'msgpack': (b'\x81\x00\x92\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + b'\x03\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + b'\x72\x72\x6f\x72\x04\x00\x05\x00\x86\x00\xab\x43' + b'\x6c\x69\x65\x6e\x74\x45\x72\x72\x6f\x72\x01\xa4' + b'\x65\x76\x61\x6c\x02\x02\x03\xad\x55\x6e\x6b\x6e' b'\x6f\x77\x6e\x20\x65\x72\x72\x6f\x72\x04\x00\x05\x00'), 'tarantool': "chained_error", 'ignore_file_info': False, @@ -254,19 +254,18 @@ def setUp(self): errcode=0, ), ), - 'msgpack': (b'\x81\x00\x92\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + - b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + - b'\x03\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + - b'\x72\x72\x6f\x72\x04\x00\x05\x00\x86\x00\xab\x43' + - b'\x6c\x69\x65\x6e\x74\x45\x72\x72\x6f\x72\x01\xa4' + - b'\x65\x76\x61\x6c\x02\x02\x03\xad\x55\x6e\x6b\x6e' + + 'msgpack': (b'\x81\x00\x92\x86\x00\xab\x43\x6c\x69\x65\x6e\x74' + b'\x45\x72\x72\x6f\x72\x01\xa4\x65\x76\x61\x6c\x02' + b'\x03\x03\xad\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x65' + b'\x72\x72\x6f\x72\x04\x00\x05\x00\x86\x00\xab\x43' + b'\x6c\x69\x65\x6e\x74\x45\x72\x72\x6f\x72\x01\xa4' + b'\x65\x76\x61\x6c\x02\x02\x03\xad\x55\x6e\x6b\x6e' b'\x6f\x77\x6e\x20\x65\x72\x72\x6f\x72\x04\x00\x05\x00'), 'tarantool': "chained_error", 'ignore_file_info': False, } } - def test_msgpack_decode(self): for name, case in self.cases.items(): with self.subTest(msg=name): @@ -325,7 +324,6 @@ def test_tarantool_decode(self): self.assertEqual(err, expected_err) - def test_msgpack_encode(self): for name, case in self.cases.items(): with self.subTest(msg=name): @@ -390,7 +388,6 @@ def test_tarantool_encode(self): self.assertSequenceEqual(conn.eval(lua_eval), [True]) - @classmethod def tearDownClass(cls): cls.conn_encoding_utf8.close() diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index d6508925..77f2cf52 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -17,6 +17,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test + class TestSuiteInterval(unittest.TestCase): @classmethod def setUpClass(cls): @@ -99,7 +100,7 @@ def test_interval_positional_init(self): 'datetime': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000), 'msgpack': (b'\x07\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x08\x01'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000})", }, 'nanoseconds': { @@ -110,36 +111,36 @@ def test_interval_positional_init(self): 'datetime_with_nanoseconds': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000, nsec=10000000), - 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' + + 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' b'\x00\x98\x96\x80\x08\x01'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000})", }, 'datetime_none_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000, nsec=10000000, adjust=tarantool.IntervalAdjust.NONE), - 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' + + 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' b'\x00\x98\x96\x80\x08\x01'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='none'})", }, 'datetime_excess_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000, nsec=10000000, adjust=tarantool.IntervalAdjust.EXCESS), - 'msgpack': (b'\x07\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' + + 'msgpack': (b'\x07\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' b'\x00\x98\x96\x80'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='excess'})", }, 'datetime_last_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000, nsec=10000000, adjust=tarantool.IntervalAdjust.LAST), - 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' + + 'msgpack': (b'\x08\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x07\xce' b'\x00\x98\x96\x80\x08\x02'), - 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " + + 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='last'})", }, 'all_zeroes': { @@ -152,12 +153,13 @@ def test_interval_positional_init(self): def test_msgpack_decode(self): for name, case in self.cases.items(): with self.subTest(msg=name): - self.assertEqual(unpacker_ext_hook( - 6, - case['msgpack'], - self.con._unpacker_factory(), - ), - case['python']) + self.assertEqual( + unpacker_ext_hook( + 6, + case['msgpack'], + self.con._unpacker_factory(), + ), + case['python']) @skip_or_run_datetime_test def test_tarantool_decode(self): @@ -182,7 +184,7 @@ def test_tarantool_encode(self): lua_eval = f""" local interval = {case['tarantool']} - + local tuple = box.space['test']:get('{name}') assert(tuple ~= nil) @@ -196,7 +198,6 @@ def test_tarantool_encode(self): self.assertSequenceEqual(self.adm(lua_eval), [True]) - def test_unknown_field_decode(self): case = b'\x01\x09\xce\x00\x98\x96\x80' self.assertRaisesRegex( @@ -209,7 +210,6 @@ def test_unknown_adjust_decode(self): MsgpackError, '3 is not a valid Adjust', lambda: unpacker_ext_hook(6, case, self.con._unpacker_factory())) - arithmetic_cases = { 'year': { 'arg_1': tarantool.Interval(year=2), @@ -237,9 +237,9 @@ def test_unknown_adjust_decode(self): }, 'datetime_with_nsec': { 'arg_1': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, - sec=3000, nsec=10000000), + sec=3000, nsec=10000000), 'arg_2': tarantool.Interval(year=2, month=1, day=31, hour=-3, minute=0, - sec=1000, nsec=9876543), + sec=1000, nsec=9876543), 'res_add': tarantool.Interval(year=3, month=3, day=34, hour=-2, minute=2, sec=4000, nsec=19876543), 'res_sub': tarantool.Interval(year=-1, month=1, day=-28, hour=4, minute=2, @@ -291,7 +291,6 @@ def test_tarantool_interval_subtraction(self): self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res_sub']]) - @classmethod def tearDownClass(cls): cls.con.close() diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index e7bf58ff..b82ccc0e 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -22,7 +22,7 @@ def create_server(_id): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " + + srv.admin("box.schema.user.create('test', {password = 'test', " "if_not_exists = true})") srv.admin("box.schema.user.grant('test', 'execute', 'universe')") diff --git a/test/suites/test_package.py b/test/suites/test_package.py index 7799d4fe..8d13858a 100644 --- a/test/suites/test_package.py +++ b/test/suites/test_package.py @@ -29,7 +29,6 @@ def setUpClass(cls): print(' PACKAGE '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - def test_version(self): if is_test_pure_install(): self.assertEqual(tarantool.__version__, metadata.version('tarantool')) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 2f9c855a..a701b648 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -26,19 +26,19 @@ def create_server(_id): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " + + srv.admin("box.schema.user.create('test', {password = 'test', " "if_not_exists = true})") srv.admin("box.schema.user.grant('test', 'execute', 'universe')") srv.admin("box.schema.space.create('test')") srv.admin(r"box.space.test:format({" - +r" { name = 'pk', type = 'string' }," + - r" { name = 'id', type = 'number', is_nullable = true }" + + r" { name = 'pk', type = 'string' }," + r" { name = 'id', type = 'number', is_nullable = true }" r"})") - srv.admin(r"box.space.test:create_index('pk'," + - r"{ unique = true," + + srv.admin(r"box.space.test:create_index('pk'," + r"{ unique = true," r" parts = {{field = 1, type = 'string'}}})") - srv.admin(r"box.space.test:create_index('id'," + - r"{ unique = true," + + srv.admin(r"box.space.test:create_index('id'," + r"{ unique = true," r" parts = {{field = 2, type = 'number', is_nullable=true}}})") srv.admin("box.schema.user.grant('test', 'read,write', 'space', 'test')") srv.admin("json = require('json')") @@ -69,7 +69,7 @@ def retry(self, func, count=5, timeout=0.5): for i in range(count): try: func() - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=broad-exception-caught,broad-except if i + 1 == count: raise exc @@ -229,8 +229,7 @@ def test_03_insert(self): self.pool.insert('test', ['test_03_insert_1', 1]), [['test_03_insert_1', 1]]) self.assertSequenceEqual( - self.pool.insert('test', ['test_03_insert_2', 2], - mode=tarantool.Mode.RW), + self.pool.insert('test', ['test_03_insert_2', 2], mode=tarantool.Mode.RW), [['test_03_insert_2', 2]]) conn_2 = tarantool.connect( @@ -301,8 +300,9 @@ def test_05_upsert(self): [['test_05_upsert', 3]]) self.assertSequenceEqual( - self.pool.upsert('test', ['test_05_upsert', 3], - [('+', 1, 1)], mode=tarantool.Mode.RW), []) + self.pool.upsert('test', ['test_05_upsert', 3], [('+', 1, 1)], + mode=tarantool.Mode.RW), + []) self.assertSequenceEqual( conn_1.select('test', 'test_05_upsert'), [['test_05_upsert', 4]]) @@ -334,8 +334,8 @@ def test_06_update(self): [['test_06_update_1', 4]]) self.assertSequenceEqual( - self.pool.update('test', ('test_06_update_2',), - [('=', 1, 10)], mode=tarantool.Mode.RW), + self.pool.update('test', ('test_06_update_2',), [('=', 1, 10)], + mode=tarantool.Mode.RW), [['test_06_update_2', 10]]) self.assertSequenceEqual( conn_4.select('test', 'test_06_update_2'), @@ -361,7 +361,7 @@ def test_07_replace(self): self.assertSequenceEqual( self.pool.replace('test', ['test_07_replace', 4], - mode=tarantool.Mode.RW), + mode=tarantool.Mode.RW), [['test_07_replace', 4]]) self.assertSequenceEqual( conn_4.select('test', 'test_07_replace'), @@ -401,22 +401,17 @@ def test_08_select(self): self.pool.select('test', 'test_08_select'), [['test_08_select', 3]]) self.assertSequenceEqual( - self.pool.select('test', ['test_08_select'], - mode=tarantool.Mode.ANY), + self.pool.select('test', ['test_08_select'], mode=tarantool.Mode.ANY), [['test_08_select', 3]]) self.assertSequenceEqual( - self.pool.select('test', 3, index='id', - mode=tarantool.Mode.RO), + self.pool.select('test', 3, index='id', mode=tarantool.Mode.RO), [['test_08_select', 3]]) self.assertSequenceEqual( - self.pool.select('test', [3], index='id', - mode=tarantool.Mode.PREFER_RW), + self.pool.select('test', [3], index='id', mode=tarantool.Mode.PREFER_RW), [['test_08_select', 3]]) def test_09_ping(self): - self.pool = tarantool.ConnectionPool(addrs=self.addrs, - user='test', - password='test') + self.pool = tarantool.ConnectionPool(addrs=self.addrs, user='test', password='test') with self.assertRaisesRegex(ValueError, "Please, specify 'mode' keyword argument"): self.pool.ping() @@ -480,7 +475,7 @@ def test_12_execute(self): resp = self.pool.execute( 'insert into "test" values (:pk, :id)', - { 'pk': 'test_12_execute_2', 'id': 2}, + {'pk': 'test_12_execute_2', 'id': 2}, mode=tarantool.Mode.RW) self.assertEqual(resp.affected_row_count, 1) self.assertEqual(resp.data, None) @@ -519,7 +514,7 @@ def test_13_failover(self): def expect_rw_request_execute_on_new_master(): self.assertSequenceEqual( self.pool.eval('return box.cfg.listen', mode=tarantool.Mode.RW), - [ str(self.addrs[1]['port']) ]) + [str(self.addrs[1]['port'])]) self.retry(func=expect_rw_request_execute_on_new_master) diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index b6be1ae9..6f3025f6 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -20,6 +20,7 @@ from .lib.tarantool_server import TarantoolServer + class TestSuiteProtocol(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/test/suites/test_push.py b/test/suites/test_push.py index 0a19794a..bd14455b 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -13,7 +13,7 @@ def create_server(): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " + + srv.admin("box.schema.user.create('test', {password = 'test', " "if_not_exists = true})") srv.admin("box.schema.user.grant('test', 'read,write,execute', 'universe')") @@ -94,8 +94,8 @@ def setUp(self): # Open connection, connection pool and mesh connection to instance. self.conn = tarantool.Connection(host=self.host, port=self.port, user='test', password='test') - self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}], - user='test', password='test') + self.conn_pool = tarantool.ConnectionPool([{'host': self.host, 'port': self.port}], + user='test', password='test') self.mesh_conn = tarantool.MeshConnection(host=self.host, port=self.port, user='test', password='test') diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index c1684302..1b3abe11 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -26,9 +26,11 @@ def _bind(self, obj, method_name): self._obj = obj self._method_name = method_name self._saved_method = getattr(obj, method_name) + def wrapper(_, *args, **kwargs): self._call_count += 1 return self._saved_method(*args, **kwargs) + bound_wrapper = wrapper.__get__(obj.__class__, obj) setattr(obj, method_name, bound_wrapper) @@ -51,8 +53,7 @@ def setUpClass(cls): cls.srv = TarantoolServer() cls.srv.script = 'test/suites/box.lua' cls.srv.start() - cls.srv.admin("box.schema.user.create('test', {password = 'test', " + - "if_not_exists = true})") + cls.srv.admin("box.schema.user.create('test', {password = 'test', if_not_exists = true})") cls.srv.admin("box.schema.user.grant('test', 'read,write,execute', 'universe')") # Create server_function and tester space (for fetch_schema opt testing purposes). @@ -91,8 +92,8 @@ def setUpClass(cls): user='test', password='test') cls.pool_con_schema_disable = tarantool.ConnectionPool( [{ - 'host':cls.srv.host, - 'port':cls.srv.args['primary'] + 'host': cls.srv.host, + 'port': cls.srv.args['primary'] }], user='test', password='test', fetch_schema=False) @@ -173,31 +174,23 @@ def verify_unicode_index(self, index): self.assertEqual(len(index.parts), 1) def test_01_space_bad(self): - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no space.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space(0) - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no space.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space(0) - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no space.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space('bad_name') def test_02_index_bad(self): - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no space.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_index(0, 'primary') - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no space.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_index('bad_space', 'primary') - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no index.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 'bad_index') - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no index.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 'bad_index') - with self.assertRaisesRegex(tarantool.SchemaError, - 'There\'s no index.*'): + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 3) def test_03_01_space_name__(self): @@ -417,7 +410,7 @@ def test_07_schema_version_update(self): 'input': ['tester', (1, None)], 'output': [[1, None]], }, - 'delete': { + 'delete': { 'input': ['tester', 1], 'output': [[1, None]], }, @@ -489,7 +482,7 @@ def _run_test_schema_fetch_disable(self, con, mode=None): _ = testing_function( *self.testing_methods['unavailable'][method_case]['input']) except NotSupportedError as exc: - self.assertEqual(exc.message, 'This method is not available in ' + + self.assertEqual(exc.message, 'This method is not available in ' 'connection opened with fetch_schema=False') # Testing the schemaless connection with methods # that should be available. @@ -565,12 +558,14 @@ def _run_test_schema_fetch_disable(self, con, mode=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"), + @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"), + @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, diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index d6c6ae2d..9452e83b 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -76,6 +76,7 @@ def __init__(self, self.client_password_file = client_password_file self.client_auth_type = client_auth_type + @unittest.skipIf(not is_test_ssl(), "TEST_TNT_SSL is not set.") class TestSuiteSsl(unittest.TestCase): @classmethod @@ -87,7 +88,7 @@ def setUpClass(cls): test_data_dir = os.path.join(test_suites_dir, "..", "data") cls.cert_file = os.path.join(test_data_dir, "localhost.crt") cls.invalidhost_cert_file = os.path.join(test_data_dir, - "invalidhost.crt") + "invalidhost.crt") cls.key_file = os.path.join(test_data_dir, "localhost.key") cls.key_enc_file = os.path.join(test_data_dir, "localhost.enc.key") cls.ca_file = os.path.join(test_data_dir, "ca.crt") @@ -457,13 +458,13 @@ def test_single(self): for t in testcases: with self.subTest(msg=t.name): if t.server_password is not None \ - or t.server_password_file is not None \ - or t.client_password is not None \ - or t.server_password_file is not None: + or t.server_password_file is not None \ + or t.client_password is not None \ + or t.server_password_file is not None: skip_or_run_ssl_password_test_call(self) if t.server_auth_type is not None \ - or t.client_auth_type is not None: + or t.client_auth_type is not None: skip_or_run_auth_type_test_call(self) srv = TarantoolServer( @@ -574,13 +575,13 @@ def test_pool(self): cnt = 5 with self.subTest(msg=t.name): if t.server_password is not None \ - or t.server_password_file is not None \ - or t.client_password is not None \ - or t.server_password_file is not None: + or t.server_password_file is not None \ + or t.client_password is not None \ + or t.server_password_file is not None: skip_or_run_ssl_password_test_call(self) if t.server_auth_type is not None \ - or t.client_auth_type is not None: + or t.client_auth_type is not None: skip_or_run_auth_type_test_call(self) addrs = [] @@ -699,13 +700,13 @@ def test_mesh(self): cnt = 5 with self.subTest(msg=t.name): if t.server_password is not None \ - or t.server_password_file is not None \ - or t.client_password is not None \ - or t.server_password_file is not None: + or t.server_password_file is not None \ + or t.client_password is not None \ + or t.server_password_file is not None: skip_or_run_ssl_password_test_call(self) if t.server_auth_type is not None \ - or t.client_auth_type is not None: + or t.client_auth_type is not None: skip_or_run_auth_type_test_call(self) addrs = [] diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index f76665c0..0a9ef06b 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -16,6 +16,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_uuid_test + class TestSuiteUUID(unittest.TestCase): @classmethod def setUpClass(cls): @@ -57,7 +58,6 @@ def setUp(self): self.adm("box.space['test']:truncate()") - cases = { 'uuid_1': { 'python': uuid.UUID('ae28d4f6-076c-49dd-8227-7f9fae9592d0'), @@ -128,7 +128,6 @@ def test_tarantool_encode(self): self.assertSequenceEqual(self.con.eval(lua_eval), [True]) - @skip_or_run_uuid_test def test_primary_key(self): data = [uuid.UUID('ae28d4f6-076c-49dd-8227-7f9fae9592d0'), 'content'] @@ -136,7 +135,6 @@ def test_primary_key(self): self.assertSequenceEqual(self.con.insert('test_pk', data), [data]) self.assertSequenceEqual(self.con.select('test_pk', data[0]), [data]) - @classmethod def tearDownClass(cls): cls.con.close() From e3eb6d083963a15113bb39be5a71156e3f12e8c1 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 17:54:39 +0300 Subject: [PATCH 125/190] flake8: disable import unused in dbapi tarantool.error values import are the part of the DBAPI requirements, so they cannot be omitted. The same rule is used in pylint. Part of #270 --- tarantool/dbapi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 908355fc..3ddd203e 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -4,6 +4,7 @@ .. _PEP-249: http://www.python.org/dev/peps/pep-0249/ """ # pylint: disable=fixme,unused-import +# flake8: noqa: F401 from tarantool.connection import Connection as BaseConnection from tarantool.error import ( From aa65f67db6c494771f0998180cd343683a24bba9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 17:55:33 +0300 Subject: [PATCH 126/190] flake8: fix unused import cases Fir all remaining cases of F401 unused import. Part of #270 --- tarantool/__init__.py | 2 -- test/suites/__init__.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 277c138d..2ab17dce 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -3,8 +3,6 @@ """ # pylint: disable=too-many-arguments -import sys - from tarantool.connection import Connection from tarantool.mesh_connection import MeshConnection from tarantool.const import ( diff --git a/test/suites/__init__.py b/test/suites/__init__.py index bf07c9c4..d56b2889 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -32,8 +32,8 @@ TestSuiteMesh, TestSuiteExecute, TestSuiteDBAPI, TestSuiteEncoding, TestSuitePool, TestSuiteSsl, TestSuiteDecimal, TestSuiteUUID, TestSuiteDatetime, - TestSuiteInterval, TestSuiteErrorExt, TestSuitePush, - TestSuiteConnection, TestSuiteCrud,) + TestSuiteInterval, TestSuitePackage, TestSuiteErrorExt, + TestSuitePush, TestSuiteConnection, TestSuiteCrud,) def load_tests(loader, tests, pattern): From 13f8baca9fe3e403278fc44cd1e076a28b203c09 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 18:00:42 +0300 Subject: [PATCH 127/190] test: add codespell dependency See [1]. 1. https://pypi.org/project/pylint/ --- requirements-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-test.txt b/requirements-test.txt index 8b3b9693..94d16351 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,3 +3,4 @@ pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' pylint flake8 +codespell From 584959ab313cce21a69a2b25b473f1a5bad1bbab Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 18:01:29 +0300 Subject: [PATCH 128/190] make: additional lint with codespell Part of #270 --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 06d78a96..88c811a4 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,12 @@ install: PYTHON_FILES=tarantool test setup.py docs/source/conf.py +TEXT_FILES=README.rst docs/source/*.rst .PHONY: lint lint: python3 -m pylint --recursive=y $(PYTHON_FILES) python3 -m flake8 $(PYTHON_FILES) + codespell $(PYTHON_FILES) $(TEXT_FILES) .PHONY: test From 3be674cf2a1f007eb8598a1c797f1f7b19da6d4d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 18:13:10 +0300 Subject: [PATCH 129/190] codespell: resolve spell issues Skip autogenerated timezones since there are too many abbreviations that confuse codespell. Part of #270 --- .codespellrc | 3 +++ tarantool/connection.py | 2 +- tarantool/msgpack_ext/types/datetime.py | 4 ++-- tarantool/request.py | 2 +- tarantool/response.py | 2 +- test/suites/lib/skip.py | 2 +- test/suites/lib/tarantool_server.py | 2 +- test/suites/test_crud.py | 4 ++-- test/suites/test_execute.py | 2 +- 9 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 .codespellrc diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 00000000..2594318c --- /dev/null +++ b/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +skip = tarantool/msgpack_ext/types/timezones/timezones.py +ignore-words-list = ans,gost diff --git a/tarantool/connection.py b/tarantool/connection.py index 9551b313..b99b0924 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -469,7 +469,7 @@ class JoinState(Enum): INITIAL = 2 """ - Received inital vclock. + Received initial vclock. """ FINAL = 3 diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 0ecaa4dd..79c669b9 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -211,7 +211,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, :type tz: :obj:`str`, optional :param timestamp_since_utc_epoch: Parameter to set timestamp - convertion behavior for timezone-aware datetimes. + conversion behavior for timezone-aware datetimes. If ``False`` (default), behaves similar to Tarantool `datetime.new()`_: @@ -232,7 +232,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, Thus, if ``False``, datetime is computed from timestamp since epoch and then timezone is applied without any - convertion. In that case, + conversion. In that case, :attr:`~tarantool.Datetime.timestamp` won't be equal to initialization :paramref:`~tarantool.Datetime.params.timestamp` for all diff --git a/tarantool/request.py b/tarantool/request.py index bdbbe68a..f8a7e7c9 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -113,7 +113,7 @@ def packer_factory(conn): else: packer_kwargs['use_bin_type'] = True - # We need configured packer to work with error extention + # We need configured packer to work with error extension # type payload, but module do not provide access to self # inside extension type packers. def default(obj): diff --git a/tarantool/response.py b/tarantool/response.py index acd0cd9f..0e062c74 100644 --- a/tarantool/response.py +++ b/tarantool/response.py @@ -76,7 +76,7 @@ def unpacker_factory(conn): if msgpack.version >= (1, 0, 0): unpacker_kwargs['strict_map_key'] = False - # We need configured unpacker to work with error extention + # We need configured unpacker to work with error extension # type payload, but module do not provide access to self # inside extension type unpackers. def ext_hook(code, data): diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 47765371..64795720 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -83,7 +83,7 @@ def skip_or_run_test_tarantool_call(self, required_tt_version, msg): def skip_or_run_test_pcall_require(func, required_tt_module, msg): """ Decorator to skip or run tests depending on tarantool - module requre success or fail. + module require success or fail. Also, it can be used with the 'setUp' method for skipping the whole test suite. diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 46f3fff9..241e0c92 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -333,7 +333,7 @@ def start(self): * (MAYBE) Copy init.lua --INSIDE * Concatenate arguments and start Tarantool\\Box --DONE(prepare_args) - * Wait unitl Tarantool\\Box + * Wait until Tarantool\\Box started --DONE(wait_until_started) """ # pylint: disable=consider-using-with diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index 5c560082..d1d7e186 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -670,10 +670,10 @@ def _correct_operation_with_crud(self, testing_function, case, mode=None): *case['success']['input']['args'], ) if 'rows' in case['success']['output']: - # Case for crud responce as tarantool.crud.CrudResult obj. + # Case for crud response as tarantool.crud.CrudResult obj. self.assertEqual(resp.rows, case['success']['output']['rows']) if 'scalar' in case['success']['output']: - # Case for scalar value as crud responce, not tarantool.crud.CrudResult obj. + # Case for scalar value as crud response, not tarantool.crud.CrudResult obj. self.assertEqual(resp, case['success']['output']['scalar']) if 'operations' in case['success']['output']: # Case for statistics testing. diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 3450df3c..66fe0982 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -1,5 +1,5 @@ """ -This module tests API fo execute SQL on a server. +This module tests API for running SQL on a server. """ # pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code From 3fc917d4548925fc1b45154d3a673d1d336c83b7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:31:13 +0300 Subject: [PATCH 130/190] pylint: fix remaining compatibility issues See [1] about global bad-option-value fail to work. 1. https://github.com/pylint-dev/pylint/issues/3312 Part of #270 --- .pylintrc | 4 ---- setup.py | 1 + tarantool/connection.py | 8 ++++---- tarantool/connection_pool.py | 8 +++++--- tarantool/dbapi.py | 2 +- tarantool/mesh_connection.py | 3 +-- tarantool/msgpack_ext/packer.py | 1 + tarantool/msgpack_ext/unpacker.py | 1 + test/suites/test_encoding.py | 2 +- test/suites/test_pool.py | 4 ++-- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.pylintrc b/.pylintrc index f3baa9b8..3fd8b942 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,7 +1,3 @@ -[MAIN] -# Enable backward compatibility with pylint for pylint down to Python 3.6 one -ignore=bad-option-value - [BASIC] # Good variable names which should always be accepted, separated by a comma diff --git a/setup.py b/setup.py index bd2a697c..4ee0797c 100755 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ """ Package setup commands. """ +# pylint: disable=bad-option-value,too-many-ancestors import codecs import os diff --git a/tarantool/connection.py b/tarantool/connection.py index b99b0924..32a60051 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -1,7 +1,7 @@ """ This module provides API for interaction with a Tarantool server. """ -# pylint: disable=too-many-lines +# pylint: disable=too-many-lines,duplicate-code import os import time @@ -1005,7 +1005,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=self.ssl_password) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except exc_list.append(exc) if self.ssl_password_file is not None: @@ -1016,7 +1016,7 @@ def _ssl_load_cert_chain(self, context): keyfile=self.ssl_key_file, password=line.rstrip()) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except exc_list.append(exc) try: @@ -1029,7 +1029,7 @@ def password_raise_error(): password=password_raise_error) return - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except exc_list.append(exc) raise SslError(exc_list) diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 508460d1..1008991f 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -1,7 +1,7 @@ """ This module provides API for interaction with Tarantool servers cluster. """ -# pylint: disable=too-many-lines +# pylint: disable=too-many-lines,duplicate-code import abc import itertools @@ -167,6 +167,7 @@ class StrategyInterface(metaclass=abc.ABCMeta): """ Defines strategy to choose a pool server based on a request mode. """ + # pylint: disable=bad-option-value,super-init-not-called @classmethod def __subclasshook__(cls, subclass): @@ -210,6 +211,7 @@ class RoundRobinStrategy(StrategyInterface): """ Simple round-robin pool servers rotation. """ + # pylint: disable=bad-option-value,no-self-use,super-init-not-called def __init__(self, pool): """ @@ -372,7 +374,7 @@ class ConnectionPool(ConnectionInterface): >>> resp - ['AAAA', 'Alpha'] """ - # pylint: disable=too-many-public-methods,duplicate-code,bad-option-value,no-self-use,super-init-not-called,bad-option-value + # pylint: disable=too-many-public-methods,duplicate-code,bad-option-value,no-self-use def __init__(self, addrs, @@ -652,7 +654,7 @@ def _request_process_loop(self, key, unit, last_refresh): method = getattr(Connection, task.method_name) try: resp = method(unit.conn, *task.args, **task.kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except unit.output_queue.put(exc) else: unit.output_queue.put(resp) diff --git a/tarantool/dbapi.py b/tarantool/dbapi.py index 3ddd203e..3b4d1013 100644 --- a/tarantool/dbapi.py +++ b/tarantool/dbapi.py @@ -3,7 +3,7 @@ .. _PEP-249: http://www.python.org/dev/peps/pep-0249/ """ -# pylint: disable=fixme,unused-import +# pylint: disable=fixme,unused-import,bad-option-value,no-self-use # flake8: noqa: F401 from tarantool.connection import Connection as BaseConnection diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 0744f2f4..ebbc1a40 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -1,7 +1,7 @@ """ This module provides API for interaction with Tarantool servers cluster. """ -# pylint: disable=fixme +# pylint: disable=fixme,duplicate-code import time @@ -267,7 +267,6 @@ class MeshConnection(Connection): """ Represents a connection to a cluster of Tarantool servers. """ - # pylint: disable=duplicate-code def __init__(self, host=None, port=None, user=None, diff --git a/tarantool/msgpack_ext/packer.py b/tarantool/msgpack_ext/packer.py index 8782474e..e2c03b8b 100644 --- a/tarantool/msgpack_ext/packer.py +++ b/tarantool/msgpack_ext/packer.py @@ -3,6 +3,7 @@ .. _extension: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/ """ +# pylint: disable=duplicate-code from decimal import Decimal from uuid import UUID diff --git a/tarantool/msgpack_ext/unpacker.py b/tarantool/msgpack_ext/unpacker.py index e7c1b574..985653d5 100644 --- a/tarantool/msgpack_ext/unpacker.py +++ b/tarantool/msgpack_ext/unpacker.py @@ -3,6 +3,7 @@ .. _extension: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/ """ +# pylint: disable=duplicate-code import tarantool.msgpack_ext.decimal as ext_decimal import tarantool.msgpack_ext.uuid as ext_uuid diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index cadd2102..14a08f54 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -74,7 +74,7 @@ def setUpClass(cls): def assertNotRaises(self, func, *args, **kwargs): try: func(*args, **kwargs) - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except self.fail(f'Function raised Exception: {repr(exc)}') def setUp(self): diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index a701b648..7febf79f 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -2,7 +2,7 @@ This module tests work with a cluster of Tarantool servers through ConnectionPool. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,duplicate-code,bad-option-value,no-self-use +# pylint: disable=missing-class-docstring,missing-function-docstring,too-many-public-methods,too-many-locals,duplicate-code,bad-option-value,no-self-use import sys import time @@ -69,7 +69,7 @@ def retry(self, func, count=5, timeout=0.5): for i in range(count): try: func() - except Exception as exc: # pylint: disable=broad-exception-caught,broad-except + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except if i + 1 == count: raise exc From 100638449acb397fd0eb99fbdca01e16a8d51ed4 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:35:20 +0300 Subject: [PATCH 131/190] flake8: fix compatibility issues Part of #270 --- tarantool/__init__.py | 2 +- tarantool/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 2ab17dce..91f80e10 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -142,4 +142,4 @@ def connectmesh(addrs=({'host': 'localhost', 'port': 3301},), user=None, __all__ = ['connect', 'Connection', 'connectmesh', 'MeshConnection', 'Schema', 'Error', 'DatabaseError', 'NetworkError', 'NetworkWarning', 'SchemaError', 'dbapi', 'Datetime', 'Interval', 'IntervalAdjust', - 'ConnectionPool', 'Mode', 'BoxError',] + 'ConnectionPool', 'Mode', 'BoxError'] diff --git a/tarantool/const.py b/tarantool/const.py index 194e97a9..21a3c8a9 100644 --- a/tarantool/const.py +++ b/tarantool/const.py @@ -136,7 +136,7 @@ # Tarantool 2.10 protocol version is 3 CONNECTOR_IPROTO_VERSION = 3 # List of connector-supported features -CONNECTOR_FEATURES = [IPROTO_FEATURE_ERROR_EXTENSION,] +CONNECTOR_FEATURES = [IPROTO_FEATURE_ERROR_EXTENSION] # Authenticate with CHAP-SHA1 (Tarantool CE and EE) AUTH_TYPE_CHAP_SHA1 = "chap-sha1" From 85bd41011d8b64ba2b0cbeac061f9b779408b46d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:44:43 +0300 Subject: [PATCH 132/190] codespell: fix compatibility issues Part of #270 --- .codespellrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codespellrc b/.codespellrc index 2594318c..fe94c2d8 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] skip = tarantool/msgpack_ext/types/timezones/timezones.py -ignore-words-list = ans,gost +ignore-words-list = ans,gost,ro From b29c29cbac51df47b5036b57a90ee6aef6b2a30d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 18:15:28 +0300 Subject: [PATCH 133/190] make: run lint before tests Closes #270 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 88c811a4..25809cd8 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ lint: .PHONY: test -test: +test: lint python3 setup.py test .PHONY: test-pure-install From 555391d89ec15b345514db6b7121424ef11a838b Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:48:46 +0300 Subject: [PATCH 134/190] test: fix lint reworks Follows #270 --- test/suites/lib/skip.py | 4 ++-- test/suites/test_connection.py | 2 +- test/suites/test_pool.py | 2 +- test/suites/test_schema.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 64795720..694d8d57 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -96,10 +96,10 @@ def wrapper(self, *args, **kwargs): srv = None - if hasattr(self, 'servers'): + if hasattr(self, 'servers') and self.servers: srv = self.servers[0] - if hasattr(self, 'srv'): + if hasattr(self, 'srv') and self.srv: srv = self.srv assert srv is not None diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 8c7df685..52234608 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -157,7 +157,7 @@ def my_unpacker_factory(_): self.assertIsInstance(resp[0], tuple) def tearDown(self): - if hasattr(self, 'con'): + if self.con: self.con.close() @classmethod diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 7febf79f..7134f220 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -566,7 +566,7 @@ def test_16_is_closed(self): self.assertEqual(self.pool.is_closed(), True) def tearDown(self): - if hasattr(self, 'pool'): + if self.pool: self.pool.close() for srv in self.servers: diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 1b3abe11..91c3e217 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -44,6 +44,7 @@ def call_count(self): class TestSuiteSchemaAbstract(unittest.TestCase): # Define 'encoding' field in a concrete class. + encoding = None @classmethod def setUpClass(cls): @@ -136,7 +137,6 @@ def setUpClass(cls): box.space.constr_tester_2:create_index('I1', { parts = {'id'} }) box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} }) """) - cls.encoding = None def setUp(self): # prevent a remote tarantool from clean our session From 24aa8dee5db748a812ae9e5b041ba72512884d87 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 19:58:55 +0300 Subject: [PATCH 135/190] setup: fix lint reworks Follows #270 --- test/setup_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/setup_command.py b/test/setup_command.py index 64c285af..9df5f8cd 100755 --- a/test/setup_command.py +++ b/test/setup_command.py @@ -9,7 +9,7 @@ try: from setuptools.errors import BaseError -except ModuleNotFoundError: +except (ModuleNotFoundError, ImportError): # pylint: disable=deprecated-module from distutils.errors import DistutilsError as BaseError From 4bdbdda95361b080f511427607674833d01dd51d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 30 Mar 2023 20:09:52 +0300 Subject: [PATCH 136/190] pylint: fix Windows compatibility Follows #270 --- tarantool/connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tarantool/connection.py b/tarantool/connection.py index 32a60051..ca7109de 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -913,6 +913,7 @@ def connect_unix(self): :meta private: """ + # pylint: disable=no-member try: # If old socket already exists - close it and re-create @@ -1193,6 +1194,7 @@ def _opt_reconnect(self): :meta private: """ + # pylint: disable=no-member # **Bug in Python: timeout is an internal Python construction (???). if not self._socket: From 3f7a1456c2836aea37422443702714229eb13713 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 14 Apr 2023 15:12:13 +0300 Subject: [PATCH 137/190] internal: change datetime implementation Rework our implementation of tarantool.Datetime class. Previously it had relied on pandas.Timestamp and pandas.Timedelta. There were user complaints about pandas as a requirement since it's rather heavy. Now our implementation of datetime uses built-in datetime.datetime, datetime.timedelta and other built-in tools. It is expected that the implementation change wouldn't affect users, but some minor behavior traits were broken in this patch: - Now we rely on datetime argument validation which if differs from pandas one. For example, it doesn't allow overflows for fields. Exceptions that user may receive from internal datetime are, of course, had changed as well. - We drop the support of `__eq__` for pandas.Timestamp. We simply compared underlying pandas.Timestamp with argument one, and now it's impossible. If the feature would be required later, we may implement its comparison in some compatible way. - `__repr__` has been changed since internal representation has been changed as well. Closes #290 --- CHANGELOG.md | 10 + docs/source/conf.py | 1 - requirements.txt | 1 - rpm/SPECS/python-tarantool.spec | 1 - tarantool/msgpack_ext/types/datetime.py | 258 ++++++++++++++++-------- test/suites/test_datetime.py | 102 +++++++++- 6 files changed, 286 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab01e84..acbefdf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migrate to built-in `Warning` instead of a custom one. - Migrate to built-in `RecursionError` instead of a custom one. - Collect full exception traceback. +- Rework `tarantool.Datetime` implementation to use built-in + `datetime.datetime`. External changes are as follows. Some of them + are **breaking**. + - Package no longer depends on `pandas` (#290). + - `__repr__` has been changed. + - Input arguments are validated with `datetime.datetime` rules. + - Class is no longer expected to throw `pandas.Timestamp` + exceptions. `datetime.datetime` exceptions will + be thrown instead of them. + - Drop the support of `__eq__` operator for `pandas.Timestamp`. ## 0.12.1 - 2023-02-28 diff --git a/docs/source/conf.py b/docs/source/conf.py index e9db5edb..953c2034 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -344,7 +344,6 @@ intersphinx_mapping = { 'python': ('http://docs.python.org/', None), 'msgpack': ('https://msgpack-python.readthedocs.io/en/latest/', None), - 'pandas': ('https://pandas.pydata.org/docs/', None), 'pytz': ('https://pytz.sourceforge.net/', None), } diff --git a/requirements.txt b/requirements.txt index aa4406ce..d88dbea3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ msgpack -pandas pytz dataclasses; python_version <= '3.6' diff --git a/rpm/SPECS/python-tarantool.spec b/rpm/SPECS/python-tarantool.spec index b1dae43d..b1e5727e 100644 --- a/rpm/SPECS/python-tarantool.spec +++ b/rpm/SPECS/python-tarantool.spec @@ -29,7 +29,6 @@ Python client library for Tarantool.} %package -n python3-%{srcname} Requires: python3-msgpack -Requires: python3-pandas Requires: python3-pytz Summary: %{summary} diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 79c669b9..6b9381f9 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -3,9 +3,11 @@ """ # pylint: disable=line-too-long +from calendar import monthrange from copy import deepcopy +from datetime import datetime, timedelta +import sys -import pandas import pytz import tarantool.msgpack_ext.types.timezones as tt_timezones @@ -16,16 +18,17 @@ NSEC_IN_MKSEC = 1000 SEC_IN_MIN = 60 MONTH_IN_YEAR = 12 +_EPOCH = datetime(1970, 1, 1, tzinfo=pytz.utc) -def compute_offset(timestamp): +def compute_offset(_datetime): """ Compute timezone offset. Offset is computed each time and not stored since it could depend on current datetime value. It is expected that timestamp offset is not ``None``. - :param timestamp: Timestamp data. - :type timestamp: :class:`pandas.Timestamp` + :param _datetime: Datetime date. + :type _datetime: :class:`datetime.datetime` :return: Timezone offset, in minutes. :rtype: :obj:`int` @@ -33,7 +36,7 @@ def compute_offset(timestamp): :meta private: """ - utc_offset = timestamp.tzinfo.utcoffset(timestamp) + utc_offset = _datetime.tzinfo.utcoffset(_datetime) # `None` offset is a valid utcoffset implementation, # but it seems that pytz timezones never return `None`: @@ -75,10 +78,28 @@ def get_python_tzinfo(tz): return pytz.FixedOffset(tt_tzinfo['offset']) +def month_last_day(year, month): + """ + Get the number of the last day in month. + + :param year: Calendar year. + :type year: :obj:`int` + + :param month: Calendar month. + :type month: :obj:`int` + + :rtype: :obj:`int` + + :meta private: + """ + + return monthrange(year, month)[1] + + class Datetime(): """ Class representing Tarantool `datetime`_ info. Internals are based - on :class:`pandas.Timestamp`. + on :class:`datetime.datetime`. You can create :class:`~tarantool.Datetime` objects by using the same API as in Tarantool: @@ -164,43 +185,39 @@ def __init__(self, *, timestamp=None, year=None, month=None, :type timestamp: :obj:`float` or :obj:`int`, optional :param year: Datetime year value. Must be a valid - :class:`pandas.Timestamp` ``year`` parameter. + :class:`datetime.datetime` ``year`` parameter. Must be provided unless the object is built with :paramref:`~tarantool.Datetime.params.data` or :paramref:`~tarantool.Datetime.params.timestamp`. :type year: :obj:`int`, optional :param month: Datetime month value. Must be a valid - :class:`pandas.Timestamp` ``month`` parameter. + :class:`datetime.datetime` ``month`` parameter. Must be provided unless the object is built with :paramref:`~tarantool.Datetime.params.data` or :paramref:`~tarantool.Datetime.params.timestamp`. :type month: :obj:`int`, optional :param day: Datetime day value. Must be a valid - :class:`pandas.Timestamp` ``day`` parameter. + :class:`datetime.datetime` ``day`` parameter. Must be provided unless the object is built with :paramref:`~tarantool.Datetime.params.data` or :paramref:`~tarantool.Datetime.params.timestamp`. :type day: :obj:`int`, optional :param hour: Datetime hour value. Must be a valid - :class:`pandas.Timestamp` ``hour`` parameter. + :class:`datetime.datetime` ``hour`` parameter. :type hour: :obj:`int`, optional :param minute: Datetime minute value. Must be a valid - :class:`pandas.Timestamp` ``minute`` parameter. + :class:`datetime.datetime` ``minute`` parameter. :type minute: :obj:`int`, optional :param sec: Datetime seconds value. Must be a valid - :class:`pandas.Timestamp` ``second`` parameter. + :class:`datetime.datetime` ``second`` parameter. :type sec: :obj:`int`, optional - :param nsec: Datetime nanoseconds value. Quotient of a division - by 1000 (nanoseconds in microseconds) must be a valid - :class:`pandas.Timestamp` ``microsecond`` parameter, - remainder of a division by 1000 must be a valid - :class:`pandas.Timestamp` ``nanosecond`` parameter. + :param nsec: Datetime nanoseconds value. :type sec: :obj:`int`, optional :param tzoffset: Timezone offset. Ignored, if provided together @@ -238,7 +255,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, :paramref:`~tarantool.Datetime.params.timestamp` for all timezones with non-zero offset. - If ``True``, behaves similar to :class:`pandas.Timestamp`: + If ``True``, behaves similar to :class:`datetime.datetime`: .. code-block:: python @@ -261,11 +278,11 @@ def __init__(self, *, timestamp=None, year=None, month=None, :type timestamp_since_utc_epoch: :obj:`bool`, optional :raise: :exc:`ValueError`, :exc:`~tarantool.error.MsgpackError`, - :class:`pandas.Timestamp` exceptions + :class:`datetime.datetime` exceptions .. _datetime.new(): https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ """ - # pylint: disable=too-many-branches,too-many-locals + # pylint: disable=too-many-branches,too-many-locals,too-many-statements tzinfo = None if tz != '': @@ -291,36 +308,85 @@ def __init__(self, *, timestamp=None, year=None, month=None, if not isinstance(timestamp, int): raise ValueError('timestamp must be int if nsec provided') - total_nsec = timestamp * NSEC_IN_SEC + nsec - datetime = pandas.to_datetime(total_nsec, unit='ns') - else: - datetime = pandas.to_datetime(timestamp, unit='s') + # Tarantool may send negative nanoseconds or nanoseconds bigger + # than 999999999. datetime.datetime doesn't process overflows. + if (nsec >= NSEC_IN_SEC) or (nsec < 0): + timestamp += nsec // NSEC_IN_SEC + nsec = nsec % NSEC_IN_SEC - if not timestamp_since_utc_epoch: - self._datetime = datetime.tz_localize(tzinfo) + if (sys.platform.startswith("win")) and (timestamp < 0): + # Fails to create a datetime from negative timestamp on Windows. + _datetime = _EPOCH + timedelta(seconds=timestamp) else: - self._datetime = datetime.tz_localize(pytz.UTC).tz_convert(tzinfo) - else: + # Timezone-naive datetime objects are treated by many datetime methods + # as local times, so we represent time in UTC explicitly if not provided. + _datetime = datetime.fromtimestamp(timestamp, pytz.UTC) + if nsec is not None: - microsecond = nsec // NSEC_IN_MKSEC - nanosecond = nsec % NSEC_IN_MKSEC + _datetime = _datetime.replace(microsecond=nsec // NSEC_IN_MKSEC) + _datetime_nsec = nsec % NSEC_IN_MKSEC else: - microsecond = 0 - nanosecond = 0 + _datetime_nsec = 0 + + if tzinfo is not None: + if not timestamp_since_utc_epoch: + # It seems that there is no way to get expected behavior without + # this hack. Localizing a timezone-naive datetime built + # from the timestamp fails since it uses local timezone to mess up + # the underlying timestamp. On the other hand, you cannot localize + # a timezone-aware datetime, even UTC one. Replaces don't work since + # they are broken for pytz + datetime, see + # https://pythonhosted.org/pytz/ + _datetime = datetime.combine(_datetime.date(), _datetime.time()) + _datetime = tzinfo.localize(_datetime) + else: + _datetime = _datetime.astimezone(tzinfo) + + self._datetime = _datetime + self._datetime_nsec = _datetime_nsec + else: + # datetime does not support None as defaults, + # we support them for backward compatibility. + if hour is None: + hour = 0 + + if minute is None: + minute = 0 - self._datetime = pandas.Timestamp( - year=year, month=month, day=day, - hour=hour, minute=minute, second=sec, - microsecond=microsecond, - nanosecond=nanosecond).tz_localize(tzinfo) + if sec is None: + sec = 0 - def _interval_operation(self, other, sign=1): + overflow = None + if nsec is None: + nsec = 0 + else: + # Tarantool may send negative nanoseconds or nanoseconds bigger + # than 999999999. datetime.datetime doesn't process overflows. + if (nsec >= NSEC_IN_SEC) or (nsec < 0): + overflow = timedelta(seconds=nsec // NSEC_IN_SEC) + nsec = nsec % NSEC_IN_SEC + + _datetime = datetime(year=year, month=month, day=day, + hour=hour, minute=minute, second=sec, + microsecond=nsec // NSEC_IN_MKSEC) + if overflow is not None: + _datetime = _datetime + overflow + # tzinfo as argument on the datetime not works as expected, see + # https://pythonhosted.org/pytz/ + # Timezone-naive datetime objects are treated by many datetime methods + # as local times, so we represent time in UTC explicitly if not provided. + if tzinfo is None: + tzinfo = pytz.UTC + self._datetime = tzinfo.localize(_datetime) + self._datetime_nsec = nsec % NSEC_IN_MKSEC + + def _interval_operation(self, interval, sign=1): """ Implementation of :class:`~tarantool.Interval` addition and subtraction. - :param other: Interval to add or subtract. - :type other: :class:`~tarantool.Interval` + :param interval: Interval to add or subtract. + :type interval: :class:`~tarantool.Interval` :param sign: Right operand multiplier: ``1`` for addition, ``-1`` for subtractiom. @@ -331,37 +397,45 @@ def _interval_operation(self, other, sign=1): :meta private: """ - self_dt = self._datetime + old_dt = self._datetime + new_dt = old_dt + + new_year = old_dt.year + sign * interval.year + new_month = old_dt.month + sign * interval.month + if (new_month < 1) or (new_month - 1 > MONTH_IN_YEAR): + new_year += (new_month - 1) // MONTH_IN_YEAR + new_month = (new_month - 1) % MONTH_IN_YEAR + 1 + + new_month_last_day = month_last_day(new_year, new_month) + old_month_last_day = month_last_day(old_dt.year, old_dt.month) # https://github.com/tarantool/tarantool/wiki/Datetime-Internals#date-adjustions-and-leap-years - months = other.year * MONTH_IN_YEAR + other.month - - res = self_dt + pandas.DateOffset(months=sign * months) - - # pandas.DateOffset works exactly like Adjust.NONE - if other.adjust == Adjust.EXCESS: - if self_dt.day > res.day: - res = res + pandas.DateOffset(days=self_dt.day - res.day) - elif other.adjust == Adjust.LAST: - if self_dt.is_month_end: - # day replaces days - res = res.replace(day=res.days_in_month) - - res = res + pandas.Timedelta(weeks=sign * other.week, - days=sign * other.day, - hours=sign * other.hour, - minutes=sign * other.minute, - seconds=sign * other.sec, - microseconds=sign * (other.nsec // NSEC_IN_MKSEC), - nanoseconds=sign * (other.nsec % NSEC_IN_MKSEC)) - - if res.tzinfo is not None: - tzoffset = compute_offset(res) + if (interval.adjust == Adjust.NONE) and (new_month_last_day < new_dt.day): + new_dt = new_dt.replace(year=new_year, month=new_month, day=new_month_last_day) + elif (interval.adjust == Adjust.EXCESS) and (new_month_last_day < new_dt.day): + new_dt = new_dt.replace(year=new_year, month=new_month, day=new_month_last_day) + \ + timedelta(days=new_dt.day - new_month_last_day) + elif (interval.adjust == Adjust.LAST) and (old_dt.day == old_month_last_day): + new_dt = new_dt.replace(year=new_year, month=new_month, day=new_month_last_day) + else: + new_dt = new_dt.replace(year=new_year, month=new_month) + + nsec = self._datetime_nsec + sign * interval.nsec + new_dt = new_dt + timedelta(weeks=sign * interval.week, + days=sign * interval.day, + hours=sign * interval.hour, + minutes=sign * interval.minute, + seconds=sign * interval.sec, + microseconds=nsec // NSEC_IN_MKSEC) + new_nsec = nsec % NSEC_IN_MKSEC + + if new_dt.tzinfo is not None: + tzoffset = compute_offset(new_dt) else: tzoffset = 0 - return Datetime(year=res.year, month=res.month, day=res.day, - hour=res.hour, minute=res.minute, sec=res.second, - nsec=res.nanosecond + res.microsecond * NSEC_IN_MKSEC, + return Datetime(year=new_dt.year, month=new_dt.month, day=new_dt.day, + hour=new_dt.hour, minute=new_dt.minute, sec=new_dt.second, + nsec=new_nsec + new_dt.microsecond * NSEC_IN_MKSEC, tzoffset=tzoffset, tz=self.tz) def __add__(self, other): @@ -471,10 +545,10 @@ def __sub__(self, other): # for possible updates. if self_dt.tzinfo != other_dt.tzinfo: - other_dt = other_dt.tz_convert(self_dt.tzinfo) + other_dt = other_dt.astimezone(self_dt.tzinfo) - self_nsec = self_dt.microsecond * NSEC_IN_MKSEC + self_dt.nanosecond - other_nsec = other_dt.microsecond * NSEC_IN_MKSEC + other_dt.nanosecond + self_nsec = self_dt.microsecond * NSEC_IN_MKSEC + self._datetime_nsec + other_nsec = other_dt.microsecond * NSEC_IN_MKSEC + other._datetime_nsec return Interval( year=self_dt.year - other_dt.year, @@ -495,23 +569,44 @@ def __eq__(self, other): Datetimes are equal when underlying datetime infos are equal. :param other: Second operand. - :type other: :class:`~tarantool.Datetime` or - :class:`~pandas.Timestamp` + :type other: :class:`~tarantool.Datetime` :rtype: :obj:`bool` """ if isinstance(other, Datetime): - return self._datetime == other._datetime - if isinstance(other, pandas.Timestamp): - return self._datetime == other + return self.value == other.value return False def __str__(self): - return self._datetime.__str__() + # Based on pandas.Timestamp isofomat for backward compatibility. + # https://github.com/pandas-dev/pandas/blob/249d93e4abc59639983eb3e8fccac8382592d457/pandas/_libs/tslibs/timestamps.pyx#L1015-L1034 + base = self._datetime.isoformat(sep='T', timespec='auto') + + # Preserve explicit UTC and implicit UTC difference for backward compatibility. + implicit_utc = False + if (self._datetime.tzinfo == pytz.UTC) and (self._tz == ''): + implicit_utc = True + base = base[:-6] + + if self._datetime_nsec == 0: + return base + + if implicit_utc: + base1, base2 = base, "" + else: + base1, base2 = base[:-6], base[-6:] + + if self._datetime.microsecond: + base1 += f"{self._datetime_nsec:03d}" + else: + base1 += f".{self._datetime_nsec:09d}" + + return base1 + base2 def __repr__(self): - return f'datetime: {self._datetime.__repr__()}, tz: "{self.tz}"' + return f'datetime: {self._datetime.__repr__()}, nsec: {self._datetime_nsec}, ' + \ + f'tz: "{self.tz}"' def __copy__(self): cls = self.__class__ @@ -595,7 +690,7 @@ def nsec(self): :rtype: :obj:`int` """ - return self._datetime.value % NSEC_IN_SEC + return self._datetime.microsecond * NSEC_IN_MKSEC + self._datetime_nsec @property def timestamp(self): @@ -605,7 +700,7 @@ def timestamp(self): :rtype: :obj:`float` """ - return self._datetime.timestamp() + return self._datetime.timestamp() + self._datetime_nsec / NSEC_IN_SEC @property def tzoffset(self): @@ -637,4 +732,7 @@ def value(self): :rtype: :obj:`int` """ - return self._datetime.value + # Python sources way to get ineteger time since epoch. + # https://github.com/python/cpython/blob/a6f95941a3d686707fb38e0f37758e666f25e180/Lib/datetime.py#L1879 + seconds = (self._datetime - _EPOCH) // timedelta(0, 1) + return seconds * NSEC_IN_SEC + self.nsec diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index f02cf523..f3bffb11 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -80,11 +80,12 @@ def test_datetime_class_api(self): self.assertEqual(datetime.minute, 7) self.assertEqual(datetime.sec, 54) self.assertEqual(datetime.nsec, 308543321) - # Both Tarantool and pandas prone to precision loss for timestamp() floats - self.assertEqual(datetime.timestamp, 1661958474.308543) + # Both Tarantool and python prone to precision loss for timestamp() floats + self.assertEqual(datetime.timestamp, 1661958474.3085432) self.assertEqual(datetime.tzoffset, 180) self.assertEqual(datetime.tz, '') self.assertEqual(datetime.value, 1661958474308543321) + self.assertEqual(str(datetime), '2022-08-31T18:07:54.308543321+03:00') def test_datetime_class_api_wth_tz(self): datetime = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, @@ -97,11 +98,12 @@ def test_datetime_class_api_wth_tz(self): self.assertEqual(datetime.minute, 7) self.assertEqual(datetime.sec, 54) self.assertEqual(datetime.nsec, 308543321) - # Both Tarantool and pandas prone to precision loss for timestamp() floats - self.assertEqual(datetime.timestamp, 1661958474.308543) + # Both Tarantool and python prone to precision loss for timestamp() floats + self.assertEqual(datetime.timestamp, 1661958474.3085432) self.assertEqual(datetime.tzoffset, 180) self.assertEqual(datetime.tz, 'Europe/Moscow') self.assertEqual(datetime.value, 1661958474308543321) + self.assertEqual(str(datetime), '2022-08-31T18:07:54.308543321+03:00') datetime_class_invalid_init_cases = { 'positional_year': { @@ -528,6 +530,98 @@ def test_primary_key(self): self.assertSequenceEqual(self.con.insert('test_pk', data), [data]) self.assertSequenceEqual(self.con.select('test_pk', data[0]), [data]) + datetime_nsec_overflow_cases = { + 'overflow_datetime': { + 'arg_1': tarantool.Datetime(year=2008, month=1, day=1, nsec=1230000000), + 'arg_2': tarantool.Datetime(year=2008, month=1, day=1, sec=1, nsec=230000000), + }, + 'underflow_datetime': { + 'arg_1': tarantool.Datetime(year=2008, month=1, day=1, nsec=-123456789), + 'arg_2': tarantool.Datetime(year=2007, month=12, day=31, hour=23, minute=59, + sec=59, nsec=876543211), + }, + 'overflow_timestamp': { + 'arg_1': tarantool.Datetime(timestamp=1199145600, nsec=1230000000), + 'arg_2': tarantool.Datetime(timestamp=1199145600 + 1, nsec=230000000), + }, + 'underflow_timestamp': { + 'arg_1': tarantool.Datetime(timestamp=1199145600, nsec=-123456789), + 'arg_2': tarantool.Datetime(timestamp=1199145600 - 1, nsec=876543211), + } + } + + def test_python_datetime_nsec_overflow(self): + for name, case in self.datetime_nsec_overflow_cases.items(): + with self.subTest(msg=name): + self.assertEqual(case['arg_1'], case['arg_2']) + + datetime_str_format = { + 'date': { + 'python': tarantool.Datetime(year=2022, month=8, day=31), + 'str': '2022-08-31T00:00:00', + }, + 'date_before_1970': { + 'python': tarantool.Datetime(year=1900, month=1, day=1), + 'str': '1900-01-01T00:00:00', + }, + 'datetime_with_minutes': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7), + 'str': '2022-08-31T18:07:00', + }, + 'datetime_with_seconds': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54), + 'str': '2022-08-31T18:07:54', + }, + 'datetime_with_microseconds': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543000), + 'str': '2022-08-31T18:07:54.308543', + }, + 'datetime_with_nanoseconds': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321), + 'str': '2022-08-31T18:07:54.308543321', + }, + 'datetime_with_positive_offset': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tzoffset=180), + 'str': '2022-08-31T18:07:54.308543321+03:00', + }, + 'datetime_with_negative_offset': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tzoffset=-60), + 'str': '2022-08-31T18:07:54.308543321-01:00', + }, + 'date_with_utc_tz': { + 'python': tarantool.Datetime(year=1970, month=1, day=1, tz='UTC'), + 'str': '1970-01-01T00:00:00+00:00', + }, + 'date_with_tz': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, tz='Europe/Moscow'), + 'str': '2022-08-31T00:00:00+03:00', + }, + 'datetime_with_tz': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tz='Europe/Moscow'), + 'str': '2022-08-31T18:07:54.308543321+03:00', + }, + 'datetime_with_tz_and_offset': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tz='Europe/Moscow', tzoffset=123), + 'str': '2022-08-31T18:07:54.308543321+03:00', + }, + 'datetime_with_abbrev_tz': { + 'python': tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, + nsec=308543321, tz='MSK'), + 'str': '2022-08-31T18:07:54.308543321+03:00', + }, + } + + def test_python_datetime_string(self): + for name, case in self.datetime_str_format.items(): + with self.subTest(msg=name): + self.assertEqual(str(case['python']), case['str']) + @classmethod def tearDownClass(cls): cls.con.close() From c7113977372acb25b10cdd26ff528c868dfd567f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 17 Apr 2023 12:33:47 +0300 Subject: [PATCH 138/190] api: remove join and subscribe This is a breaking change. Current join and subscribe implementations are rather useless. Connector does not provide any API to process incoming replication requests. The only supported scenario is to "connect as replica, skip everything that has been sent through replication, close on error". Current Tarantool team product strategy is to develop CDC features, including replication support in language connectors, as Enterprise edition products [1]. Since we don't plan to provide proper join and subscribe implementation in the open-source connector in the future, we decide to drop current half-baked implementation to not confuse new users. 1. https://github.com/tarantool/go-tarantool/issues/203 --- CHANGELOG.md | 1 + tarantool/connection.py | 115 ---------------------------------------- tarantool/request.py | 62 ---------------------- 3 files changed, 1 insertion(+), 177 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acbefdf4..fb835f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 exceptions. `datetime.datetime` exceptions will be thrown instead of them. - Drop the support of `__eq__` operator for `pandas.Timestamp`. +- **Breaking**: Remove `join` and `subscribe` connection methods. ## 0.12.1 - 2023-02-28 diff --git a/tarantool/connection.py b/tarantool/connection.py index ca7109de..9e0ec0f9 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -25,21 +25,17 @@ from tarantool.response import ( unpacker_factory as default_unpacker_factory, - Response, ) from tarantool.request import ( packer_factory as default_packer_factory, Request, - # RequestOK, RequestCall, RequestDelete, RequestEval, RequestInsert, - RequestJoin, RequestReplace, RequestPing, RequestSelect, - RequestSubscribe, RequestUpdate, RequestUpsert, RequestAuthenticate, @@ -60,8 +56,6 @@ DEFAULT_SSL_CIPHERS, DEFAULT_SSL_PASSWORD, DEFAULT_SSL_PASSWORD_FILE, - REQUEST_TYPE_OK, - REQUEST_TYPE_ERROR, IPROTO_GREETING_SIZE, ITERATOR_EQ, ITERATOR_ALL, @@ -1520,61 +1514,6 @@ def _get_auth_type(self): return auth_type - def _join_v16(self, server_uuid): - """ - Execute a JOIN request for Tarantool 1.6 and older. - - :param server_uuid: UUID of Tarantool server to join. - :type server_uuid: :obj:`str` - - :raise: :exc:`~AssertionError`, - :exc:`~tarantool.error.DatabaseError`, - :exc:`~tarantool.error.SchemaError`, - :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` - """ - - request = RequestJoin(self, server_uuid) - self._socket.sendall(bytes(request)) - - while True: - resp = Response(self, self._read_response()) - yield resp - if resp.code == REQUEST_TYPE_OK or resp.code >= REQUEST_TYPE_ERROR: - return - self.close() # close connection after JOIN - - def _join_v17(self, server_uuid): - """ - Execute a JOIN request for Tarantool 1.7 and newer. - - :param server_uuid: UUID of Tarantool server to join. - :type server_uuid: :obj:`str` - - :raise: :exc:`~AssertionError`, - :exc:`~tarantool.error.DatabaseError`, - :exc:`~tarantool.error.SchemaError`, - :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` - """ - - request = RequestJoin(self, server_uuid) - self._socket.sendall(bytes(request)) - state = JoinState.HANDSHAKE - while True: - resp = Response(self, self._read_response()) - yield resp - if resp.code >= REQUEST_TYPE_ERROR: - return - if resp.code == REQUEST_TYPE_OK: - if state == JoinState.HANDSHAKE: - state = JoinState.INITIAL - elif state == JoinState.INITIAL: - state = JoinState.FINAL - elif state == JoinState.FINAL: - state = JoinState.DONE - return - def _ops_process(self, space, update_ops): new_ops = [] for operation in update_ops: @@ -1584,60 +1523,6 @@ def _ops_process(self, space, update_ops): new_ops.append(operation) return new_ops - def join(self, server_uuid): - """ - Execute a JOIN request: `join`_ a replicaset. - - :param server_uuid: UUID of connector "server". - :type server_uuid: :obj:`str` - - :raise: :exc:`~AssertionError`, - :exc:`~tarantool.error.DatabaseError`, - :exc:`~tarantool.error.SchemaError`, - :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` - - .. _join: https://www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol/#iproto-join-0x41 - """ - - self._opt_reconnect() - if self.version_id < version_id(1, 7, 0): - return self._join_v16(server_uuid) - return self._join_v17(server_uuid) - - def subscribe(self, cluster_uuid, server_uuid, vclock=None): - """ - Execute a SUBSCRIBE request: `subscribe`_ to a replicaset - updates. Connection is closed after subscribing. - - :param cluster_uuid: UUID of replicaset cluster. - :type cluster_uuid: :obj:`str` - - :param server_uuid: UUID of connector "server". - :type server_uuid: :obj:`str` - - :param vclock: Connector "server" vclock. - :type vclock: :obj:`dict` or :obj:`None`, optional - - :raise: :exc:`~AssertionError`, - :exc:`~tarantool.error.DatabaseError`, - :exc:`~tarantool.error.SchemaError`, - :exc:`~tarantool.error.NetworkError`, - :exc:`~tarantool.error.SslError` - - .. _subscribe: https://www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol/#iproto-subscribe-0x42 - """ - - vclock = vclock or {} - request = RequestSubscribe(self, cluster_uuid, server_uuid, vclock) - self._socket.sendall(bytes(request)) - while True: - resp = Response(self, self._read_response()) - yield resp - if resp.code >= REQUEST_TYPE_ERROR: - return - self.close() # close connection after SUBSCRIBE - def insert(self, space_name, values, *, on_push=None, on_push_ctx=None): """ Execute an INSERT request: `insert`_ a tuple to the space. diff --git a/tarantool/request.py b/tarantool/request.py index f8a7e7c9..d8d4dd22 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -20,12 +20,8 @@ IPROTO_TUPLE, IPROTO_FUNCTION_NAME, IPROTO_ITERATOR, - IPROTO_SERVER_UUID, - IPROTO_CLUSTER_UUID, - IPROTO_VCLOCK, IPROTO_EXPR, IPROTO_OPS, - # IPROTO_INDEX_BASE, IPROTO_SCHEMA_ID, IPROTO_SQL_TEXT, IPROTO_SQL_BIND, @@ -44,8 +40,6 @@ REQUEST_TYPE_EXECUTE, REQUEST_TYPE_EVAL, REQUEST_TYPE_AUTHENTICATE, - REQUEST_TYPE_JOIN, - REQUEST_TYPE_SUBSCRIBE, REQUEST_TYPE_ID, AUTH_TYPE_CHAP_SHA1, AUTH_TYPE_PAP_SHA256, @@ -587,62 +581,6 @@ def __init__(self, conn, space_no, index_no, tuple_value, op_list): self._body = request_body -class RequestJoin(Request): - """ - Represents JOIN request. - """ - - request_type = REQUEST_TYPE_JOIN - - def __init__(self, conn, server_uuid): - """ - :param conn: Request sender. - :type conn: :class:`~tarantool.Connection` - - :param server_uuid: UUID of connector "server". - :type server_uuid: :obj:`str` - """ - - super().__init__(conn) - request_body = self._dumps({IPROTO_SERVER_UUID: server_uuid}) - self._body = request_body - - -class RequestSubscribe(Request): - """ - Represents SUBSCRIBE request. - """ - - request_type = REQUEST_TYPE_SUBSCRIBE - - def __init__(self, conn, cluster_uuid, server_uuid, vclock): - """ - :param conn: Request sender. - :type conn: :class:`~tarantool.Connection` - - :param server_uuid: UUID of connector "server". - :type server_uuid: :obj:`str` - - :param server_uuid: UUID of connector "server". - :type server_uuid: :obj:`str` - - :param vclock: Connector "server" vclock. - :type vclock: :obj:`dict` - - :raise: :exc:`~AssertionError` - """ - - super().__init__(conn) - assert isinstance(vclock, dict) - - request_body = self._dumps({ - IPROTO_CLUSTER_UUID: cluster_uuid, - IPROTO_SERVER_UUID: server_uuid, - IPROTO_VCLOCK: vclock - }) - self._body = request_body - - class RequestOK(Request): """ Represents OK acknowledgement. From f0f3054271440252f7d9a3b0c16c81dd67fc60ba Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 17 Apr 2023 12:54:12 +0300 Subject: [PATCH 139/190] release 1.0.0 Overview This release introduces several minor behavior changes to make API more consistent. Starting from this release, connector no longer depends on `pandas`. Breaking changes - Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. - `tarantool.Datetime` `__repr__` has been changed. - `tarantool.Datetime` input arguments are validated with `datetime.datetime` rules. - `tarantool.Datetime` is no longer expected to throw `pandas.Timestamp` exceptions. `datetime.datetime` exceptions will be thrown instead of them. - Drop the support of `__eq__` operator of `tarantool.Datetime` for `pandas.Timestamp`. - Remove `join` and `subscribe` connection methods. Changes - Migrate to built-in `Warning` instead of a custom one. - Migrate to built-in `RecursionError` instead of a custom one. - Collect full exception traceback. - Package no longer depends on `pandas` (#290). Infrastructure - Lint the code with `pylint`, `flake8` and `codespell`. --- CHANGELOG.md | 2 +- debian/changelog | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb835f03..8f887898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. 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 +## 1.0.0 - 2023-04-17 ### Changed - **Breaking**: Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. diff --git a/debian/changelog b/debian/changelog index c85ff501..21241d50 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,35 @@ +python3-tarantool (1.0.0-0) unstable; urgency=medium + + ## Overview + + This release introduces several minor behavior changes + to make API more consistent. + + Starting from this release, connector no longer depends on `pandas`. + + ## Breaking changes + + - Allow only named `on_push` and `on_push_ctx` for `insert` and `replace`. + - `tarantool.Datetime` `__repr__` has been changed. + - `tarantool.Datetime` input arguments are validated with `datetime.datetime` rules. + - `tarantool.Datetime` is no longer expected to throw `pandas.Timestamp` + exceptions. `datetime.datetime` exceptions will be thrown instead of them. + - Drop the support of `__eq__` operator of `tarantool.Datetime` for `pandas.Timestamp`. + - Remove `join` and `subscribe` connection methods. + + ## Changes + + - Migrate to built-in `Warning` instead of a custom one. + - Migrate to built-in `RecursionError` instead of a custom one. + - Collect full exception traceback. + - Package no longer depends on `pandas` (#290). + + ## Infrastructure + + - Lint the code with `pylint`, `flake8` and `codespell`. + + -- Georgy.moiseev Mon, 17 Apr 2023 13:00:00 +0300 + python3-tarantool (0.12.1-0) unstable; urgency=medium ## Overview From b073ae6b70cb4938a71ba1f5f29235a3d2da1f4a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 18 May 2023 16:08:29 +0300 Subject: [PATCH 140/190] test: fix compatibility with Tarantool master New Tarantool 3.0 patch introduces a breaking change [1]. This patch adds a compatibility layer to test instance. 1. https://github.com/tarantool/tarantool/pull/8289 Closes #293 --- test/suites/crud_server.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/suites/crud_server.lua b/test/suites/crud_server.lua index 55d4fc48..8bb58fcb 100644 --- a/test/suites/crud_server.lua +++ b/test/suites/crud_server.lua @@ -1,5 +1,13 @@ #!/usr/bin/env tarantool +local function replicaset_uuid() + if box.info().replicaset ~= nil and box.info().replicaset.uuid ~= nil then + return box.info().replicaset.uuid + end + + return box.info().cluster.uuid +end + local function configure_crud_instance(primary_listen, crud, vshard) box.schema.create_space( 'tester', { @@ -30,7 +38,7 @@ local function configure_crud_instance(primary_listen, crud, vshard) local cfg = { bucket_count = 300, sharding = { - [box.info().cluster.uuid] = { + [replicaset_uuid()] = { replicas = { [box.info().uuid] = { uri = uri, From ef8cfd94f4f0b1f7f211ad017600c3b77a035317 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 24 May 2023 18:41:03 +0300 Subject: [PATCH 141/190] test: fix compatibility with Tarantool master New Tarantool 3.0 patch introduces a breaking change [1]. This patch adds a compatibility layer to test instance. It was decided to explicitly set SEQSCAN in our tests and change session options for DBAPI tests. 1. https://github.com/tarantool/tarantool/pull/8602 --- test/suites/box.lua | 6 ++++++ test/suites/lib/tarantool_server.py | 9 +++++++-- test/suites/test_dbapi.py | 4 +++- test/suites/test_schema.py | 19 +++++++++++++++---- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/test/suites/box.lua b/test/suites/box.lua index f551bd1c..eb4330dd 100644 --- a/test/suites/box.lua +++ b/test/suites/box.lua @@ -1,11 +1,17 @@ #!/usr/bin/env tarantool +local is_compat, compat = pcall(require, "compat") local os = require('os') local admin_listen = os.getenv("ADMIN") local primary_listen = os.getenv("LISTEN") local auth_type = os.getenv("AUTH_TYPE") +local SQL_SEQ_SCAN_DEFAULT = os.getenv("SQL_SEQ_SCAN_DEFAULT") +if is_compat and SQL_SEQ_SCAN_DEFAULT then + compat.sql_seq_scan_default = SQL_SEQ_SCAN_DEFAULT +end + require('console').listen(admin_listen) box.cfg{ listen = primary_listen, diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 241e0c92..1dd5e053 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -189,7 +189,8 @@ def __new__(cls, ssl_password=None, ssl_password_file=None, create_unix_socket=False, - auth_type=None): + auth_type=None, + sql_seq_scan_default=None): # pylint: disable=unused-argument if os.name == 'nt': @@ -205,7 +206,8 @@ def __init__(self, ssl_password=None, ssl_password_file=None, create_unix_socket=False, - auth_type=None): + auth_type=None, + sql_seq_scan_default=None): # pylint: disable=consider-using-with os.popen('ulimit -c unlimited').close() @@ -235,6 +237,7 @@ def __init__(self, self.ssl_password = ssl_password self.ssl_password_file = ssl_password_file self.auth_type = auth_type + self.sql_seq_scan_default = sql_seq_scan_default self._binary = None self._log_des = None @@ -289,6 +292,8 @@ def generate_configuration(self): os.putenv("AUTH_TYPE", self.auth_type) else: os.putenv("AUTH_TYPE", "") + if self.sql_seq_scan_default is not None: + os.putenv("SQL_SEQ_SCAN_DEFAULT", self.sql_seq_scan_default) def prepare_args(self): """ diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 65799960..83156b39 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -27,7 +27,9 @@ class TestSuiteDBAPI(dbapi20.DatabaseAPI20Test): def setUpClass(cls): print(' DBAPI '.center(70, '='), file=sys.stderr) print('-' * 70, file=sys.stderr) - cls.srv = TarantoolServer() + # Select scans are not allowed with compat.sql_seq_scan_default = "new", + # but tests create cursors with fullscan. + cls.srv = TarantoolServer(sql_seq_scan_default="old") cls.srv.script = 'test/suites/box.lua' cls.srv.start() cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 91c3e217..1402616d 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -454,10 +454,21 @@ def test_07_schema_version_update(self): def _run_test_schema_fetch_disable(self, con, mode=None): # Enable SQL test case for tarantool 2.* and higher. if int(str(self.srv.admin.tnt_version)[0]) > 1: - self.testing_methods['available']['execute'] = { - 'input': ['SELECT * FROM "tester"'], - 'output': [[1, None]], - } + if self.srv.admin.tnt_version >= pkg_resources.parse_version('2.11.0'): + # SEQSCAN keyword is explicitly allowing to use seqscan + # https://github.com/tarantool/tarantool/commit/77648827326ad268ec0ffbcd620c2371b65ef2b4 + # It was introduced in 2.11.0-rc1. If compat.sql_seq_scan_default + # set to "new" (default value since 3.0), returns error + # if trying to scan without keyword. + self.testing_methods['available']['execute'] = { + 'input': ['SELECT * FROM SEQSCAN "tester"'], + 'output': [[1, None]], + } + else: + self.testing_methods['available']['execute'] = { + 'input': ['SELECT * FROM "tester"'], + 'output': [[1, None]], + } # Testing the schemaless connection with methods # that should NOT be available. From 00efc10bc1258114338d23b9c08c6a386f6f5e03 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 24 May 2023 18:47:25 +0300 Subject: [PATCH 142/190] ci: test with Tarantool 2.11 --- .github/workflows/packing.yml | 2 +- .github/workflows/testing.yml | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index 17118fe6..5932316e 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -81,7 +81,7 @@ jobs: - name: Install tarantool uses: tarantool/setup-tarantool@v2 with: - tarantool-version: '2.10' + tarantool-version: '2.11' - name: Download pip package artifacts uses: actions/download-artifact@v3 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c49f8b92..3bc47556 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -26,6 +26,7 @@ jobs: - '1.10' - '2.8' - '2.10' + - '2.11' python: - '3.6' - '3.7' @@ -119,8 +120,8 @@ jobs: path: '' - bundle: 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64' path: '' - - bundle: 'sdk-gc64-2.11.0-entrypoint-113-g803baaffe-r529.linux.x86_64' - path: 'dev/linux/x86_64/master/' + - bundle: 'sdk-gc64-2.11.0-rc2-0-r557.linux.x86_64' + path: 'release/linux/x86_64/2.11/' python: ['3.6', '3.7', '3.8', '3.9', '3.10'] steps: @@ -169,7 +170,7 @@ jobs: make test env: TEST_TNT_SSL: ${{ matrix.tarantool.bundle == 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64' || - matrix.tarantool.bundle == 'sdk-gc64-2.11.0-entrypoint-113-g803baaffe-r529.linux.x86_64'}} + matrix.tarantool.bundle == 'sdk-gc64-2.11.0-rc2-0-r557.linux.x86_64'}} run_tests_pip_branch_install_linux: # We want to run on external PRs, but not on our own internal @@ -187,7 +188,7 @@ jobs: matrix: tarantool: - - '2.10' + - '2.11' python: - '3.6' - '3.7' From 7848ac7957775a0a00f5d3cb6e1668b4e1b0de06 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 26 Jun 2023 17:43:44 +0300 Subject: [PATCH 143/190] iproto: provide new features codes Pagination was added in Tarantool 2.11.0-rc1 [1]. Space and index names support will be added to Tarantool 3.0 [2]. Watch once feature will be added to Tarantool 3.0 [3]. 1. https://github.com/tarantool/tarantool/commit/948e5cdce18b081a8f7b03ebd43e34a029b7aefe 2. https://github.com/tarantool/tarantool/commit/b9550f192673581dcbd8432867ae2e55ec264676 3. https://github.com/tarantool/tarantool/commit/6dc1433a58bbefad187f6d08531245e5a36415f1 Part of #267 --- tarantool/const.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tarantool/const.py b/tarantool/const.py index 21a3c8a9..1e2b0895 100644 --- a/tarantool/const.py +++ b/tarantool/const.py @@ -99,6 +99,9 @@ IPROTO_FEATURE_TRANSACTIONS = 1 IPROTO_FEATURE_ERROR_EXTENSION = 2 IPROTO_FEATURE_WATCHERS = 3 +IPROTO_FEATURE_PAGINATION = 4 +IPROTO_FEATURE_SPACE_AND_INDEX_NAMES = 5 +IPROTO_FEATURE_WATCH_ONCE = 6 # Default value for connection timeout (seconds) CONNECTION_TIMEOUT = None @@ -133,8 +136,8 @@ # Default delay between attempts to reconnect (seconds) POOL_INSTANCE_RECONNECT_DELAY = 0 -# Tarantool 2.10 protocol version is 3 -CONNECTOR_IPROTO_VERSION = 3 +# Tarantool master 970ea48 protocol version is 6 +CONNECTOR_IPROTO_VERSION = 6 # List of connector-supported features CONNECTOR_FEATURES = [IPROTO_FEATURE_ERROR_EXTENSION] From b0c18a3d0e7c123eb796f9d1ad63d732d1184180 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 27 Jun 2023 15:43:53 +0300 Subject: [PATCH 144/190] conn: store client and server protocol info Store client and server protocol version and features in connection object, similar to go-tarantool [1]. Before the patch, we stored only products: minimal protocol version of server and client and the list of features supported both by client and server. 1. https://github.com/tarantool/go-tarantool/pull/226 Part of #267 --- tarantool/connection.py | 54 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 9e0ec0f9..79603db2 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -20,6 +20,7 @@ import ctypes.util from ctypes import c_ssize_t from typing import Optional, Union +from copy import copy import msgpack @@ -838,6 +839,10 @@ def __init__(self, host, port, self.version_id = None self.uuid = None self._salt = None + self._client_protocol_version = CONNECTOR_IPROTO_VERSION + self._client_features = copy(CONNECTOR_FEATURES) + self._server_protocol_version = None + self._server_features = None if connect_now: self.connect() @@ -1044,10 +1049,11 @@ def handshake(self): if greeting.protocol != "Binary": raise NetworkError("Unsupported protocol: " + greeting.protocol) self.version_id = greeting.version_id - if self.version_id >= version_id(2, 10, 0): - self._check_features() self.uuid = greeting.uuid self._salt = greeting.salt + + self._check_features() + if self.user: self.authenticate(self.user, self.password) @@ -2057,32 +2063,28 @@ def _check_features(self): :exc:`~tarantool.error.SslError` """ - try: - request = RequestProtocolVersion(self, - CONNECTOR_IPROTO_VERSION, - CONNECTOR_FEATURES) - response = self._send_request(request) - server_protocol_version = response.protocol_version - server_features = response.features - server_auth_type = response.auth_type - except DatabaseError as exc: - if exc.code == ER_UNKNOWN_REQUEST_TYPE: - server_protocol_version = None - server_features = [] - server_auth_type = None - else: - raise exc - - if server_protocol_version is not None: - self._protocol_version = min(server_protocol_version, - CONNECTOR_IPROTO_VERSION) + if self.version_id >= version_id(2, 10, 0): + try: + request = RequestProtocolVersion(self, + self._client_protocol_version, + self._client_features) + response = self._send_request(request) + self._server_protocol_version = response.protocol_version + self._server_features = response.features + self._server_auth_type = response.auth_type + except DatabaseError as exc: + if exc.code != ER_UNKNOWN_REQUEST_TYPE: + raise exc + + if self._server_protocol_version is not None: + self._protocol_version = min(self._server_protocol_version, + self._client_protocol_version) # Intercept lists of features - features_list = [val for val in CONNECTOR_FEATURES if val in server_features] - for val in features_list: - self._features[val] = True - - self._server_auth_type = server_auth_type + if self._server_features is not None: + features_list = [val for val in self._client_features if val in self._server_features] + for val in features_list: + self._features[val] = True def _packer_factory(self): return self._packer_factory_impl(self) From e954c4cc448cd3164c1a63564e827955d5079732 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 27 Jun 2023 17:04:51 +0300 Subject: [PATCH 145/190] api: allow to require protocol version and features Allow to set required_protocol_version and required_features on connection initialization to ensure that a Tarantool server provides expected features. The approach is similar to go-tarantool [1]. We do not check client protocol version and features, similar to the core Tarantool [1, 2]. 1. https://github.com/tarantool/go-tarantool/pull/226 2. https://github.com/tarantool/tarantool/issues/7953 Closes #267 --- CHANGELOG.md | 5 +++++ tarantool/connection.py | 41 ++++++++++++++++++++++++++++++++++-- test/suites/lib/skip.py | 16 ++++++++++++++ test/suites/test_protocol.py | 34 ++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f887898..0f1e78b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Added +- Allow to require specific server protocol version and features (#267). + ## 1.0.0 - 2023-04-17 ### Changed diff --git a/tarantool/connection.py b/tarantool/connection.py index 79603db2..ea089c08 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -66,6 +66,9 @@ IPROTO_FEATURE_TRANSACTIONS, IPROTO_FEATURE_ERROR_EXTENSION, IPROTO_FEATURE_WATCHERS, + IPROTO_FEATURE_PAGINATION, + IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, + IPROTO_FEATURE_WATCH_ONCE, IPROTO_CHUNK, AUTH_TYPE_CHAP_SHA1, AUTH_TYPE_PAP_SHA256, @@ -608,7 +611,9 @@ def __init__(self, host, port, packer_factory=default_packer_factory, unpacker_factory=default_unpacker_factory, auth_type=None, - fetch_schema=True): + fetch_schema=True, + required_protocol_version=None, + required_features=None): """ :param host: Server hostname or IP address. Use ``None`` for Unix sockets. @@ -777,6 +782,14 @@ def __init__(self, host, port, :meth:`~tarantool.Connection.space`. :type fetch_schema: :obj:`bool`, optional + :param required_protocol_version: Minimal protocol version that + should be supported by Tarantool server. + :type required_protocol_version: :obj:`int` or :obj:`None`, optional + + :param required_features: List of protocol features that + should be supported by Tarantool server. + :type required_features: :obj:`list` or :obj:`None`, optional + :raise: :exc:`~tarantool.error.ConfigurationError`, :meth:`~tarantool.Connection.connect` exceptions @@ -785,7 +798,7 @@ def __init__(self, host, port, .. _mp_bin: https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family .. _mp_array: https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family """ - # pylint: disable=too-many-arguments,too-many-locals + # pylint: disable=too-many-arguments,too-many-locals,too-many-statements if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'): raise ConfigurationError("msgpack>=1.0.0 only supports None and " @@ -831,6 +844,9 @@ def __init__(self, host, port, IPROTO_FEATURE_TRANSACTIONS: False, IPROTO_FEATURE_ERROR_EXTENSION: False, IPROTO_FEATURE_WATCHERS: False, + IPROTO_FEATURE_PAGINATION: False, + IPROTO_FEATURE_SPACE_AND_INDEX_NAMES: False, + IPROTO_FEATURE_WATCH_ONCE: False, } self._packer_factory_impl = packer_factory self._unpacker_factory_impl = unpacker_factory @@ -843,6 +859,8 @@ def __init__(self, host, port, self._client_features = copy(CONNECTOR_FEATURES) self._server_protocol_version = None self._server_features = None + self.required_protocol_version = required_protocol_version + self.required_features = copy(required_features) if connect_now: self.connect() @@ -2076,6 +2094,25 @@ def _check_features(self): if exc.code != ER_UNKNOWN_REQUEST_TYPE: raise exc + if self.required_protocol_version is not None: + if self._server_protocol_version is None or \ + self._server_protocol_version < self.required_protocol_version: + raise ConfigurationError('Server protocol version is ' + f'{self._server_protocol_version}, ' + f'protocol version {self.required_protocol_version} ' + 'is required') + + if self.required_features is not None: + if self._server_features is None: + failed_features = self.required_features + else: + failed_features = [val for val in self.required_features + if val not in self._server_features] + + if len(failed_features) > 0: + str_features = ', '.join([str(v) for v in failed_features]) + raise ConfigurationError(f'Server missing protocol features with id {str_features}') + if self._server_protocol_version is not None: self._protocol_version = min(self._server_protocol_version, self._client_protocol_version) diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 694d8d57..e370f7a1 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -277,3 +277,19 @@ def skip_or_run_constraints_test(func): return skip_or_run_test_tarantool(func, '2.10.0', 'does not support schema constraints') + + +def skip_or_run_iproto_basic_features_test(func): + """ + Decorator to skip or run tests related to iproto ID requests, + protocol version and features. + + Tarantool supports iproto ID requests only since 2.10.0 version. + Protocol version is 3 for Tarantool 2.10.0, + IPROTO_FEATURE_STREAMS, IPROTO_FEATURE_TRANSACTIONS + and IPROTO_FEATURE_ERROR_EXTENSION are supported in Tarantool 2.10.0. + See https://github.com/tarantool/tarantool/issues/6253 + """ + + return skip_or_run_test_tarantool(func, '2.10.0', + 'does not support iproto ID and iproto basic features') diff --git a/test/suites/test_protocol.py b/test/suites/test_protocol.py index 6f3025f6..004f0ea0 100644 --- a/test/suites/test_protocol.py +++ b/test/suites/test_protocol.py @@ -15,10 +15,15 @@ IPROTO_FEATURE_TRANSACTIONS, IPROTO_FEATURE_ERROR_EXTENSION, IPROTO_FEATURE_WATCHERS, + IPROTO_FEATURE_PAGINATION, + IPROTO_FEATURE_SPACE_AND_INDEX_NAMES, + IPROTO_FEATURE_WATCH_ONCE, ) +from tarantool.error import NetworkError from tarantool.utils import greeting_decode, version_id from .lib.tarantool_server import TarantoolServer +from .lib.skip import skip_or_run_iproto_basic_features_test class TestSuiteProtocol(unittest.TestCase): @@ -91,6 +96,35 @@ def test_04_protocol(self): self.assertEqual(self.con._features[IPROTO_FEATURE_STREAMS], False) self.assertEqual(self.con._features[IPROTO_FEATURE_TRANSACTIONS], False) self.assertEqual(self.con._features[IPROTO_FEATURE_WATCHERS], False) + self.assertEqual(self.con._features[IPROTO_FEATURE_PAGINATION], False) + self.assertEqual(self.con._features[IPROTO_FEATURE_SPACE_AND_INDEX_NAMES], False) + self.assertEqual(self.con._features[IPROTO_FEATURE_WATCH_ONCE], False) + + @skip_or_run_iproto_basic_features_test + def test_protocol_requirement(self): + try: + con = tarantool.Connection(self.srv.host, self.srv.args['primary'], + required_protocol_version=3, + required_features=[IPROTO_FEATURE_STREAMS, + IPROTO_FEATURE_TRANSACTIONS, + IPROTO_FEATURE_ERROR_EXTENSION]) + con.close() + except Exception as exc: # pylint: disable=bad-option-value,broad-exception-caught,broad-except + self.fail(f'Connection create have raised Exception: {repr(exc)}') + + def test_protocol_version_requirement_fail(self): + with self.assertRaisesRegex(NetworkError, # ConfigurationError is wrapped in NetworkError + 'protocol version 100500 is required'): + con = tarantool.Connection(self.srv.host, self.srv.args['primary'], + required_protocol_version=100500) + con.close() + + def test_protocol_features_requirement_fail(self): + with self.assertRaisesRegex(NetworkError, # ConfigurationError is wrapped in NetworkError + 'Server missing protocol features with id 100500, 500100'): + con = tarantool.Connection(self.srv.host, self.srv.args['primary'], + required_features=[100500, 500100]) + con.close() @classmethod def tearDownClass(cls): From ae0d7f74d7527072e05bd41fa52246105ab38303 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 28 Jun 2023 12:34:04 +0300 Subject: [PATCH 146/190] test: run cases for datetime with 2.11 fixes There were a couple of datetime bugs in core Tarantool [1, 2] in 2.10 that were fixed in 2.11. We had a couple of test cases which were skipped before because of these bugs. After 2.11 we may change these skips to conditional skips. 1. https://github.com/tarantool/tarantool/issues/7698 2. https://github.com/tarantool/tarantool/issues/7700 Closes #246 --- tarantool/msgpack_ext/types/datetime.py | 12 ------------ test/suites/lib/skip.py | 13 +++++++++++++ test/suites/test_datetime.py | 17 +++++++++-------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 6b9381f9..1a546cfc 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -532,18 +532,6 @@ def __sub__(self, other): self_dt = self._datetime other_dt = other._datetime - # Tarantool datetime subtraction ignores timezone info, but it is a bug: - # - # Tarantool 2.10.1-0-g482d91c66 - # - # tarantool> datetime.new{tz='MSK'} - datetime.new{tz='UTC'} - # --- - # - +0 seconds - # ... - # - # Refer to https://github.com/tarantool/tarantool/issues/7698 - # for possible updates. - if self_dt.tzinfo != other_dt.tzinfo: other_dt = other_dt.astimezone(self_dt.tzinfo) diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index e370f7a1..00b1a21d 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -205,6 +205,19 @@ def skip_or_run_datetime_test(func): 'does not support datetime type') +def skip_or_run_datetime_2_11_test(func): + """ + Decorator to skip or run tests related to datetime module with + fixes introduced in 2.11 release. + + See https://github.com/tarantool/tarantool/issues/7698 and + https://github.com/tarantool/tarantool/issues/7700 + """ + + return skip_or_run_test_tarantool(func, '2.11.0', + 'does not provide required datetime fixes') + + def skip_or_run_error_extra_info_test(func): """ Decorator to skip or run tests related to extra error info diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index f3bffb11..40f84215 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -15,7 +15,7 @@ from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook from .lib.tarantool_server import TarantoolServer -from .lib.skip import skip_or_run_datetime_test +from .lib.skip import skip_or_run_datetime_test, skip_or_run_datetime_2_11_test class TestSuiteDatetime(unittest.TestCase): @@ -381,21 +381,23 @@ def test_tarantool_datetime_subtraction(self): datetime_subtraction_different_timezones_case = { 'arg_1': tarantool.Datetime(year=2001, month=2, day=3, tz='UTC'), 'arg_2': tarantool.Datetime(year=2001, month=2, day=3, tz='MSK'), - 'res': tarantool.Interval(day=1, hour=-21), + # Tarantool datetime comparison is naive, our tarantool.Interval comparison is naive too. + # So even though day=1, hour=-21 is the same as minute=180, test assertion fails. + 'res_python': tarantool.Interval(day=1, hour=-21), + 'res_tarantool': tarantool.Interval(minute=180), } def test_python_datetime_subtraction_different_timezones(self): case = self.datetime_subtraction_different_timezones_case - self.assertEqual(case['arg_1'] - case['arg_2'], case['res']) + self.assertEqual(case['arg_1'] - case['arg_2'], case['res_python']) - @skip_or_run_datetime_test - @unittest.skip('See https://github.com/tarantool/tarantool/issues/7698') + @skip_or_run_datetime_2_11_test def test_tarantool_datetime_subtraction_different_timezones(self): case = self.datetime_subtraction_different_timezones_case self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), - [case['res']]) + [case['res_tarantool']]) interval_arithmetic_cases = { 'year': { @@ -515,8 +517,7 @@ def test_python_datetime_addition_winter_time_switch(self): self.assertEqual(case['arg_1'] + case['arg_2'], case['res']) - @skip_or_run_datetime_test - @unittest.skip('See https://github.com/tarantool/tarantool/issues/7700') + @skip_or_run_datetime_2_11_test def test_tarantool_datetime_addition_winter_time_switch(self): case = self.datetime_addition_winter_time_switch_case From 9c8945f9be0d83950e760713f12ecf216a73b795 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 28 Jun 2023 13:39:04 +0300 Subject: [PATCH 147/190] ci: bump versions Add Tarantool 2.11, newest SDKs and Python 3.11. Reduce test matrices where possible. --- .github/workflows/packing.yml | 8 ++-- .github/workflows/reusable_testing.yml | 2 +- .github/workflows/testing.yml | 60 +++++++++++--------------- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index 5932316e..eabf5096 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Python and basic packing tools uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Install tools for packing and verification run: pip3 install wheel twine @@ -73,7 +73,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Remove connector source code run: python3 .github/scripts/remove_source_code.py @@ -128,7 +128,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Remove connector source code run: python3 .github/scripts/remove_source_code.py @@ -196,7 +196,7 @@ jobs: - name: Setup Python and basic packing tools uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Install tools for package publishing run: pip3 install twine diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index f1b594eb..e9441532 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -31,7 +31,7 @@ jobs: - name: Setup python3 for tests uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: '3.11' - name: Install connector requirements run: pip3 install -r requirements.txt diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 3bc47556..25aa6b30 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -33,6 +33,7 @@ jobs: - '3.8' - '3.9' - '3.10' + - '3.11' msgpack-deps: # latest msgpack will be installed as a part of requirements.txt - '' @@ -42,17 +43,17 @@ jobs: # "This page is taking too long to load." error. Thus we use # pairwise testing. include: - - tarantool: '2.8' - python: '3.10' + - tarantool: '2.11' + python: '3.11' msgpack-deps: 'msgpack-python==0.4.0' - - tarantool: '2.8' - python: '3.10' + - tarantool: '2.11' + python: '3.11' msgpack-deps: 'msgpack==0.5.0' - - tarantool: '2.8' - python: '3.10' + - tarantool: '2.11' + python: '3.11' msgpack-deps: 'msgpack==0.6.2' - - tarantool: '2.8' - python: '3.10' + - tarantool: '2.11' + python: '3.11' msgpack-deps: 'msgpack==1.0.4' steps: @@ -114,15 +115,15 @@ jobs: fail-fast: false matrix: tarantool: - - bundle: 'bundle-1.10.11-0-gf0b0e7ecf-r470' - path: '' - - bundle: 'bundle-2.8.3-21-g7d35cd2be-r470' - path: '' - - bundle: 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64' - path: '' - - bundle: 'sdk-gc64-2.11.0-rc2-0-r557.linux.x86_64' + - bundle: 'sdk-1.10.15-0-r563' + path: 'release/linux/x86_64/1.10/' + - bundle: 'sdk-2.8.4-0-r563' + path: 'release/linux/x86_64/2.8/' + - bundle: 'sdk-gc64-2.10.7-0-r563.linux.x86_64' + path: 'release/linux/x86_64/2.10/' + - bundle: 'sdk-gc64-2.11.0-0-r563.linux.x86_64' path: 'release/linux/x86_64/2.11/' - python: ['3.6', '3.7', '3.8', '3.9', '3.10'] + python: ['3.6', '3.11'] steps: - name: Clone the connector @@ -169,8 +170,8 @@ jobs: source tarantool-enterprise/env.sh make test env: - TEST_TNT_SSL: ${{ matrix.tarantool.bundle == 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64' || - matrix.tarantool.bundle == 'sdk-gc64-2.11.0-rc2-0-r557.linux.x86_64'}} + TEST_TNT_SSL: ${{ matrix.tarantool.bundle == 'sdk-gc64-2.10.7-0-r563.linux.x86_64' || + matrix.tarantool.bundle == 'sdk-gc64-2.11.0-0-r563.linux.x86_64'}} run_tests_pip_branch_install_linux: # We want to run on external PRs, but not on our own internal @@ -191,10 +192,7 @@ jobs: - '2.11' python: - '3.6' - - '3.7' - - '3.8' - - '3.9' - - '3.10' + - '3.11' steps: - name: Clone the connector repo uses: actions/checkout@v3 @@ -244,12 +242,10 @@ jobs: matrix: # Use reduced test matrix cause Windows pipelines are long. tarantool: - - '1.10' - - '2.8' - - '2.10.0.g0a5ce0b9c-1' + - '2.11.0.g247a9a418-1' python: - '3.6' - - '3.10' + - '3.11' steps: - name: Clone the connector @@ -271,15 +267,7 @@ jobs: with: distribution: Ubuntu-20.04 - - name: Install tarantool ${{ matrix.tarantool }} for WSL (2.8 and older) - if: (matrix.tarantool == '1.10') || (matrix.tarantool == '2.8') - shell: wsl-bash_Ubuntu-20.04 {0} - run: | - curl -L https://tarantool.io/installer.sh | VER=${{ matrix.tarantool }} bash -s -- --type "release" - sudo apt install -y tarantool tarantool-dev - - name: Install tarantool ${{ matrix.tarantool }} for WSL (2.10 and newer) - if: (matrix.tarantool != '1.10') && (matrix.tarantool != '2.8') shell: wsl-bash_Ubuntu-20.04 {0} run: | curl -L https://tarantool.io/release/2/installer.sh | bash -s @@ -324,10 +312,10 @@ jobs: matrix: # Use reduced test matrix cause Windows pipelines are long. tarantool: - - '2.10.0.g0a5ce0b9c-1' + - '2.11.0.g247a9a418-1' python: - '3.6' - - '3.10' + - '3.11' steps: - name: Clone the connector repo uses: actions/checkout@v3 From b866691739201f9b7427a484a4001b68ad800691 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 29 Jun 2023 15:27:44 +0300 Subject: [PATCH 148/190] msgpack: support decimals with negative scale Current decimal external type parser do not expect negative scale in decimal payload. Negative scale is a positive exponent. It seems that the only way to obtain positive exponent is to use E-notation since Tarantool library do not truncate trailing zeroes before decimal point: ``` tarantool> msgpack.encode(decimal.new('1e33')):hex() --- - c70301d0df1c ... tarantool> msgpack.encode(decimal.new('1000000000000000000000000000000000')):hex() --- - c713010001000000000000000000000000000000000c ... ``` There are two different bugs in current implementation: - we support only `positive fixint` scale and do not expect `int 8` [2], - we do not expect negative scale so positive exponent will be ignored. This patch fixes both of them. See also [3]. 1. https://github.com/tarantool/tarantool/blob/ba749e820bf0638aa3f79f266848590f9713c1cf/src/lib/core/decimal.c#L432-L450 2. https://github.com/msgpack/msgpack/blob/master/spec.md 3. https://github.com/tarantool/go-tarantool/pull/314 --- CHANGELOG.md | 3 +++ tarantool/msgpack_ext/decimal.py | 19 +++++++++++++++--- test/suites/test_decimal.py | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1e78b8..68408037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Allow to require specific server protocol version and features (#267). +### Fixed +- Parsing of E-notation Tarantool decimals with positive exponent (PR #298). + ## 1.0.0 - 2023-04-17 ### Changed diff --git a/tarantool/msgpack_ext/decimal.py b/tarantool/msgpack_ext/decimal.py index 545ee07d..f1654784 100644 --- a/tarantool/msgpack_ext/decimal.py +++ b/tarantool/msgpack_ext/decimal.py @@ -56,6 +56,8 @@ from decimal import Decimal +import msgpack + from tarantool.error import MsgpackError, MsgpackWarning, warn EXT_ID = 1 @@ -353,7 +355,12 @@ def decode(data, _): :raise: :exc:`~tarantool.error.MsgpackError` """ - scale = data[0] + # A decimal starts with mp_int or mp_uint followed by raw bytes. + unpacker = msgpack.Unpacker() + unpacker.feed(data) + + scale = unpacker.unpack() + scale_size = unpacker.tell() sign = get_str_sign(data[-1] & 0x0f) @@ -362,7 +369,7 @@ def decode(data, _): add_str_digit(data[-1] >> 4, digits_reverted, scale) - for i in range(len(data) - 2, 0, -1): + for i in range(len(data) - 2, scale_size - 1, -1): add_str_digit(data[i] & 0x0f, digits_reverted, scale) add_str_digit(data[i] >> 4, digits_reverted, scale) @@ -372,6 +379,12 @@ def decode(data, _): digits_reverted.append(sign) - str_repr = ''.join(digits_reverted[::-1]) + digits = digits_reverted[::-1] + + # Add trailing zeroes in case of a negative scale + for i in range(0, -1 * scale): + add_str_digit(0, digits, scale) + + str_repr = ''.join(digits) return Decimal(str_repr) diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index c817ad08..b880eaa3 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -226,6 +226,39 @@ def setUp(self): b'\x09\x87\x65\x43\x21\x98\x76\x54\x32\x1d'), 'tarantool': "decimal.new('-1234567891234567890.0987654321987654321')", }, + 'decimal_exponent_1': { + 'python': decimal.Decimal('1e33'), + 'msgpack': (b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0c'), + 'tarantool': "decimal.new('1e33')", + }, + 'decimal_exponent_2': { + 'python': decimal.Decimal('1.2345e33'), + 'msgpack': (b'\x00\x01\x23\x45\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0c'), + 'tarantool': "decimal.new('1.2345e33')", + }, + 'decimal_exponent_3': { + 'python': decimal.Decimal('1.2345e2'), + 'msgpack': (b'\x02\x12\x34\x5c'), + 'tarantool': "decimal.new('1.2345e2')", + }, + 'decimal_exponent_4': { + 'python': decimal.Decimal('1.2345e4'), + 'msgpack': (b'\x00\x12\x34\x5c'), + 'tarantool': "decimal.new('1.2345e4')", + }, + 'decimal_exponent_5': { + 'python': decimal.Decimal('-1e33'), + 'msgpack': (b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0d'), + 'tarantool': "decimal.new('-1e33')", + }, + 'decimal_exponent_6': { + 'python': decimal.Decimal('1e-33'), + 'msgpack': (b'\x21\x1c'), + 'tarantool': "decimal.new('1e-33')", + }, } def test_msgpack_decode(self): From 71f140f8d978b230363b49c13d9811328ad68e7d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 29 Jun 2023 16:34:08 +0300 Subject: [PATCH 149/190] deps: drop msgpack-python support msgpack-python package was deprecated in 2018 in favor of msgpack package. After previous patch in the patchset, we require `Unpacker` `tell` handle and msgpack-python 0.4.0 do not yet have it. It seems to be no reason to support the package which is not supported for the five years already, so we better drop it. --- .github/workflows/testing.yml | 3 --- CHANGELOG.md | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 25aa6b30..199744f2 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -43,9 +43,6 @@ jobs: # "This page is taking too long to load." error. Thus we use # pairwise testing. include: - - tarantool: '2.11' - python: '3.11' - msgpack-deps: 'msgpack-python==0.4.0' - tarantool: '2.11' python: '3.11' msgpack-deps: 'msgpack==0.5.0' diff --git a/CHANGELOG.md b/CHANGELOG.md index 68408037..255846bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Allow to require specific server protocol version and features (#267). +### Changed +- Drop `msgpack-python` support. Use `msgpack` instead. + ### Fixed - Parsing of E-notation Tarantool decimals with positive exponent (PR #298). From 2ed19f1e1becc07d87d9bb5d2b4413a4d46dd18f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 30 Jun 2023 11:39:07 +0300 Subject: [PATCH 150/190] deb: fix changelog entry --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 21241d50..9591f277 100644 --- a/debian/changelog +++ b/debian/changelog @@ -28,7 +28,7 @@ python3-tarantool (1.0.0-0) unstable; urgency=medium - Lint the code with `pylint`, `flake8` and `codespell`. - -- Georgy.moiseev Mon, 17 Apr 2023 13:00:00 +0300 + -- Georgy Moiseev Mon, 17 Apr 2023 13:00:00 +0300 python3-tarantool (0.12.1-0) unstable; urgency=medium From 1427094c12d849c091784ea897116c13327badb8 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 30 Jun 2023 10:31:34 +0300 Subject: [PATCH 151/190] release 1.1.0 Overview This release introduces API to request server protocol version and feature, as well as introduce decimal bugfix. Breaking changes - Drop `msgpack-python` support. (Package not supported since 2019.) Use `msgpack` instead. Added - Allow to require specific server protocol version and features (#267). Fixed - Parsing of E-notation Tarantool decimals with positive exponent (PR #298). --- CHANGELOG.md | 2 +- debian/changelog | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 255846bc..3ad980b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. 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 +## 1.1.0 - 2023-06-30 ### Added - Allow to require specific server protocol version and features (#267). diff --git a/debian/changelog b/debian/changelog index 9591f277..3d653acf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,23 @@ +python3-tarantool (1.1.0-0) unstable; urgency=medium + + ## Overview + + This release introduces API to request server protocol version and + feature, as well as introduce decimal bugfix. + + ## Breaking changes + + - Drop `msgpack-python` support. (Package not supported since 2019.) + Use `msgpack` instead. + + ## Added + - Allow to require specific server protocol version and features (#267). + + ## Fixed + - Parsing of E-notation Tarantool decimals with positive exponent (PR #298). + + -- Georgy Moiseev Fri, 30 Jun 2023 10:00:00 +0300 + python3-tarantool (1.0.0-0) unstable; urgency=medium ## Overview From 6512af8b26b9661235f178776059014b9b423a61 Mon Sep 17 00:00:00 2001 From: Georgiy Lebedev Date: Sat, 1 Jul 2023 13:49:17 +0300 Subject: [PATCH 152/190] test: fix `box.tuple.new` variable argument syntax usage Variable argument syntax of `box.tuple.new` will be disabled in scope of tarantool/tarantool#4693: adapt the DML test suite to stop using it. --- test/suites/test_dml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 0263d451..69293b55 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -210,7 +210,7 @@ def test_07_call_16(self): self.assertEqual(len(ans[0]), 1) self.assertIsInstance(ans[0][0], str) - self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), + self.assertSequenceEqual(con.call('box.tuple.new', [[1, 2, 3, 'fld_1']]), [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) finally: @@ -236,7 +236,7 @@ def test_07_call_17(self): self.assertEqual(len(ans), 1) self.assertIsInstance(ans[0], str) - self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), + self.assertSequenceEqual(con.call('box.tuple.new', [[1, 2, 3, 'fld_1']]), [[1, 2, 3, 'fld_1']]) self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) From 97c1c37a5753d92605ae236651efd8c35ff34e5d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 13 Jul 2023 19:31:29 +0300 Subject: [PATCH 153/190] doc: add week to interval example --- tarantool/msgpack_ext/types/interval.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index 10dc4847..c5bec39e 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -49,8 +49,8 @@ class Interval(): .. code-block:: python - di = tarantool.Interval(year=-1, month=2, day=3, - hour=4, minute=-5, sec=6, + di = tarantool.Interval(year=-1, month=2, week=-3, + day=4, hour=5, minute=-6, sec=7, nsec=308543321, adjust=tarantool.IntervalAdjust.NONE) From 778f2b77754bef4761eea83efbebdeefdaa770dd Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 13 Jul 2023 19:32:23 +0300 Subject: [PATCH 154/190] test: add week interval cases Add Interval with week encoding test cases and datetime arithmetic week test case. --- test/suites/test_datetime.py | 6 ++++++ test/suites/test_interval.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 40f84215..ae22dcfc 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -480,6 +480,12 @@ def test_tarantool_datetime_subtraction_different_timezones(self): 'res_add': tarantool.Datetime(year=2009, month=3, day=31), 'res_sub': tarantool.Datetime(year=2009, month=1, day=31), }, + 'week': { + 'arg_1': tarantool.Datetime(year=2008, month=2, day=3), + 'arg_2': tarantool.Interval(week=1), + 'res_add': tarantool.Datetime(year=2008, month=2, day=10), + 'res_sub': tarantool.Datetime(year=2008, month=1, day=27), + }, } def test_python_interval_addition(self): diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 77f2cf52..1821e901 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -148,6 +148,24 @@ def test_interval_positional_init(self): 'msgpack': (b'\x00'), 'tarantool': r"datetime.interval.new({adjust='excess'})", }, + 'weeks': { + 'python': tarantool.Interval(week=3), + 'msgpack': (b'\x02\x02\x03\x08\x01'), + 'tarantool': r"datetime.interval.new({week=3})", + }, + 'date_with_week': { + 'python': tarantool.Interval(year=1, month=2, week=3, day=4), + 'msgpack': (b'\x05\x00\x01\x01\x02\x02\x03\x03\x04\x08\x01'), + 'tarantool': r"datetime.interval.new({year=1, month=2, week=3, day=4})", + }, + 'datetime_with_week': { + 'python': tarantool.Interval(year=1, month=2, week=3, day=4, hour=1, minute=2, + sec=3000, nsec=10000000), + 'msgpack': (b'\x09\x00\x01\x01\x02\x02\x03\x03\x04\x04\x01\x05\x02\x06\xcd\x0b\xb8' + b'\x07\xce\x00\x98\x96\x80\x08\x01'), + 'tarantool': r"datetime.interval.new({year=1, month=2, week=3, day=4, hour=1, " + r"min=2, sec=3000, nsec=10000000})", + }, } def test_msgpack_decode(self): From 1607a4603bf3c7058dd3aa9054d2147165b8ffcc Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 13 Jul 2023 19:31:17 +0300 Subject: [PATCH 155/190] interval: fix week arithmetic Before this patch, weeks were ignored in Interval addition and subtraction. This patch fixes the issue. --- CHANGELOG.md | 5 +++++ tarantool/msgpack_ext/types/interval.py | 2 ++ test/suites/test_interval.py | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad980b7..84b8a35b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Fixed +- `tarantool.Interval` arithmetic with weeks + ## 1.1.0 - 2023-06-30 ### Added diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index c5bec39e..c1cf7189 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -145,6 +145,7 @@ def __add__(self, other): return Interval( year=self.year + other.year, month=self.month + other.month, + week=self.week + other.week, day=self.day + other.day, hour=self.hour + other.hour, minute=self.minute + other.minute, @@ -194,6 +195,7 @@ def __sub__(self, other): return Interval( year=self.year - other.year, month=self.month - other.month, + week=self.week - other.week, day=self.day - other.day, hour=self.hour - other.hour, minute=self.minute - other.minute, diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 1821e901..8474620a 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -283,6 +283,28 @@ def test_unknown_adjust_decode(self): 'res_add': tarantool.Interval(year=3, adjust=tarantool.IntervalAdjust.LAST), 'res_sub': tarantool.Interval(year=1, adjust=tarantool.IntervalAdjust.LAST), }, + 'weeks': { + 'arg_1': tarantool.Interval(week=2), + 'arg_2': tarantool.Interval(week=1), + 'res_add': tarantool.Interval(week=3), + 'res_sub': tarantool.Interval(week=1), + }, + 'date_with_week': { + 'arg_1': tarantool.Interval(year=1, month=2, week=3, day=4), + 'arg_2': tarantool.Interval(year=4, month=3, week=2, day=1), + 'res_add': tarantool.Interval(year=5, month=5, week=5, day=5), + 'res_sub': tarantool.Interval(year=-3, month=-1, week=1, day=3), + }, + 'datetime_with_week': { + 'arg_1': tarantool.Interval(year=1, month=2, week=3, day=4, hour=1, minute=2, + sec=3000, nsec=10000000), + 'arg_2': tarantool.Interval(year=2, month=1, week=-1, day=31, hour=-3, minute=0, + sec=1000, nsec=9876543), + 'res_add': tarantool.Interval(year=3, month=3, week=2, day=35, hour=-2, minute=2, + sec=4000, nsec=19876543), + 'res_sub': tarantool.Interval(year=-1, month=1, week=4, day=-27, hour=4, minute=2, + sec=2000, nsec=123457), + }, } def test_python_interval_addition(self): From 6eec0e773012cb03099fad6f56532fa60bd7b641 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Thu, 13 Jul 2023 19:44:57 +0300 Subject: [PATCH 156/190] interval: display weeks in str and repr --- CHANGELOG.md | 1 + tarantool/msgpack_ext/types/interval.py | 4 +-- test/suites/test_interval.py | 38 +++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b8a35b..35b5dcdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `tarantool.Interval` arithmetic with weeks +- `tarantool.Interval` weeks display in `str()` and `repr()` ## 1.1.0 - 2023-06-30 diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index c1cf7189..54551a18 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -233,8 +233,8 @@ def __eq__(self, other): return True def __repr__(self): - return f'tarantool.Interval(year={self.year}, month={self.month}, day={self.day}, ' + \ - f'hour={self.hour}, minute={self.minute}, sec={self.sec}, ' + \ + return f'tarantool.Interval(year={self.year}, month={self.month}, week={self.week}, ' + \ + f'day={self.day}, hour={self.hour}, minute={self.minute}, sec={self.sec}, ' + \ f'nsec={self.nsec}, adjust={self.adjust})' __str__ = __repr__ diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 8474620a..6917b49c 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -71,42 +71,58 @@ def test_interval_positional_init(self): 'python': tarantool.Interval(year=1), 'msgpack': (b'\x02\x00\x01\x08\x01'), 'tarantool': r"datetime.interval.new({year=1})", + 'str': 'tarantool.Interval(year=1, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'big_year': { 'python': tarantool.Interval(year=1000), 'msgpack': (b'\x02\x00\xcd\x03\xe8\x08\x01'), 'tarantool': r"datetime.interval.new({year=1000})", + 'str': 'tarantool.Interval(year=1000, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'date': { 'python': tarantool.Interval(year=1, month=2, day=3), 'msgpack': (b'\x04\x00\x01\x01\x02\x03\x03\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'big_month_date': { 'python': tarantool.Interval(year=1, month=100000, day=3), 'msgpack': (b'\x04\x00\x01\x01\xce\x00\x01\x86\xa0\x03\x03\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=100000, day=3})", + 'str': 'tarantool.Interval(year=1, month=100000, week=0, day=3, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'time': { 'python': tarantool.Interval(hour=1, minute=2, sec=3), 'msgpack': (b'\x04\x04\x01\x05\x02\x06\x03\x08\x01'), 'tarantool': r"datetime.interval.new({hour=1, min=2, sec=3})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=1, ' + 'minute=2, sec=3, nsec=0, adjust=Adjust.NONE)', }, 'big_seconds_time': { 'python': tarantool.Interval(hour=1, minute=2, sec=3000), 'msgpack': (b'\x04\x04\x01\x05\x02\x06\xcd\x0b\xb8\x08\x01'), 'tarantool': r"datetime.interval.new({hour=1, min=2, sec=3000})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=1, ' + 'minute=2, sec=3000, nsec=0, adjust=Adjust.NONE)', }, 'datetime': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, sec=3000), 'msgpack': (b'\x07\x00\x01\x01\x02\x03\x03\x04\x01\x05\x02\x06\xcd\x0b\xb8\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=1, ' + 'minute=2, sec=3000, nsec=0, adjust=Adjust.NONE)', }, 'nanoseconds': { 'python': tarantool.Interval(nsec=10000000), 'msgpack': (b'\x02\x07\xce\x00\x98\x96\x80\x08\x01'), 'tarantool': r"datetime.interval.new({nsec=10000000})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=0, nsec=10000000, adjust=Adjust.NONE)', }, 'datetime_with_nanoseconds': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, @@ -115,6 +131,8 @@ def test_interval_positional_init(self): b'\x00\x98\x96\x80\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=1, ' + 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.NONE)', }, 'datetime_none_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, @@ -124,6 +142,8 @@ def test_interval_positional_init(self): b'\x00\x98\x96\x80\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='none'})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=1, ' + 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.NONE)', }, 'datetime_excess_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, @@ -133,6 +153,8 @@ def test_interval_positional_init(self): b'\x00\x98\x96\x80'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='excess'})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=1, ' + 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.EXCESS)', }, 'datetime_last_adjust': { 'python': tarantool.Interval(year=1, month=2, day=3, hour=1, minute=2, @@ -142,21 +164,29 @@ def test_interval_positional_init(self): b'\x00\x98\x96\x80\x08\x02'), 'tarantool': r"datetime.interval.new({year=1, month=2, day=3, hour=1, " r"min=2, sec=3000, nsec=10000000, adjust='last'})", + 'str': 'tarantool.Interval(year=1, month=2, week=0, day=3, hour=1, ' + 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.LAST)', }, 'all_zeroes': { 'python': tarantool.Interval(adjust=tarantool.IntervalAdjust.EXCESS), 'msgpack': (b'\x00'), 'tarantool': r"datetime.interval.new({adjust='excess'})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.EXCESS)', }, 'weeks': { 'python': tarantool.Interval(week=3), 'msgpack': (b'\x02\x02\x03\x08\x01'), 'tarantool': r"datetime.interval.new({week=3})", + 'str': 'tarantool.Interval(year=0, month=0, week=3, day=0, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'date_with_week': { 'python': tarantool.Interval(year=1, month=2, week=3, day=4), 'msgpack': (b'\x05\x00\x01\x01\x02\x02\x03\x03\x04\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, week=3, day=4})", + 'str': 'tarantool.Interval(year=1, month=2, week=3, day=4, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', }, 'datetime_with_week': { 'python': tarantool.Interval(year=1, month=2, week=3, day=4, hour=1, minute=2, @@ -165,6 +195,8 @@ def test_interval_positional_init(self): b'\x07\xce\x00\x98\x96\x80\x08\x01'), 'tarantool': r"datetime.interval.new({year=1, month=2, week=3, day=4, hour=1, " r"min=2, sec=3000, nsec=10000000})", + 'str': 'tarantool.Interval(year=1, month=2, week=3, day=4, hour=1, ' + 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.NONE)', }, } @@ -216,6 +248,12 @@ def test_tarantool_encode(self): self.assertSequenceEqual(self.adm(lua_eval), [True]) + def test_class_string(self): + for name, case in self.cases.items(): + with self.subTest(msg=name): + self.assertEqual(str(case['python']), case['str']) + self.assertEqual(repr(case['python']), case['str']) + def test_unknown_field_decode(self): case = b'\x01\x09\xce\x00\x98\x96\x80' self.assertRaisesRegex( From 5a50fe4c60580f1273c5d23f2ac8287443533585 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 17 Jul 2023 17:35:14 +0300 Subject: [PATCH 157/190] interval: validate limits Before this patch, any value was allowed for interval attributes. Now we use the same rules as in Tarantool. A couple of issues were met while developing this patch, follow [1, 2] for core updates. 1. https://github.com/tarantool/tarantool/issues/8878 2. https://github.com/tarantool/tarantool/issues/8887 --- CHANGELOG.md | 3 + tarantool/msgpack_ext/types/interval.py | 58 ++++ test/suites/test_interval.py | 369 ++++++++++++++++++++++++ 3 files changed, 430 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b5dcdc..1e9aff0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed +- Validate `tarantool.Interval` limits with the same rules as in Tarantool. + ### Fixed - `tarantool.Interval` arithmetic with weeks - `tarantool.Interval` weeks display in `str()` and `repr()` diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index 54551a18..e44dcce0 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -16,6 +16,60 @@ 8: 'adjust', } +# https://github.com/tarantool/tarantool/blob/ff57f990f359f6d7866c1947174d8ba0e97b1ea6/src/lua/datetime.lua#L112-L146 +SECS_PER_DAY = 86400 + +MIN_DATE_YEAR = -5879610 +MIN_DATE_MONTH = 6 +MIN_DATE_DAY = 22 +MAX_DATE_YEAR = 5879611 +MAX_DATE_MONTH = 7 +MAX_DATE_DAY = 11 + +AVERAGE_DAYS_YEAR = 365.25 +AVERAGE_WEEK_YEAR = AVERAGE_DAYS_YEAR / 7 +INT_MAX = 2147483647 +MAX_YEAR_RANGE = MAX_DATE_YEAR - MIN_DATE_YEAR +MAX_MONTH_RANGE = MAX_YEAR_RANGE * 12 +MAX_WEEK_RANGE = MAX_YEAR_RANGE * AVERAGE_WEEK_YEAR +MAX_DAY_RANGE = MAX_YEAR_RANGE * AVERAGE_DAYS_YEAR +MAX_HOUR_RANGE = MAX_DAY_RANGE * 24 +MAX_MIN_RANGE = MAX_HOUR_RANGE * 60 +MAX_SEC_RANGE = MAX_DAY_RANGE * SECS_PER_DAY +MAX_NSEC_RANGE = INT_MAX + +max_val = { + 'year': MAX_YEAR_RANGE, + 'month': MAX_MONTH_RANGE, + 'week': MAX_WEEK_RANGE, + 'day': MAX_DAY_RANGE, + 'hour': MAX_HOUR_RANGE, + 'minute': MAX_MIN_RANGE, + 'sec': MAX_SEC_RANGE, + 'nsec': MAX_NSEC_RANGE, +} + + +def verify_range(intv): + """ + Check allowed values. Approach is the same as in tarantool/tarantool. + + :param intv: Raw interval to verify. + :type intv: :class:`~tarantool.Interval` + + :raise: :exc:`ValueError` + + :meta private: + """ + + for field_name, range_max in max_val.items(): + val = getattr(intv, field_name) + # Tarantool implementation has a bug + # https://github.com/tarantool/tarantool/issues/8878 + if (val > range_max) or (val < -range_max): + raise ValueError(f"value {val} of {field_name} is out of " + f"allowed range [{-range_max}, {range_max}]") + # https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.h#L34 class Adjust(Enum): @@ -92,6 +146,8 @@ def __init__(self, *, year=0, month=0, week=0, :param adjust: Interval adjustment rule. Refer to :meth:`~tarantool.Datetime.__add__`. :type adjust: :class:`~tarantool.IntervalAdjust`, optional + + :raise: :exc:`ValueError` """ self.year = year @@ -104,6 +160,8 @@ def __init__(self, *, year=0, month=0, week=0, self.nsec = nsec self.adjust = adjust + verify_range(self) + def __add__(self, other): """ Valid operations: diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index 6917b49c..b2726776 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -13,6 +13,16 @@ from tarantool.error import MsgpackError from tarantool.msgpack_ext.packer import default as packer_default from tarantool.msgpack_ext.unpacker import ext_hook as unpacker_ext_hook +from tarantool.msgpack_ext.types.interval import ( + MAX_YEAR_RANGE, + MAX_MONTH_RANGE, + MAX_WEEK_RANGE, + MAX_DAY_RANGE, + MAX_HOUR_RANGE, + MAX_MIN_RANGE, + MAX_SEC_RANGE, + MAX_NSEC_RANGE, +) from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test @@ -198,6 +208,262 @@ def test_interval_positional_init(self): 'str': 'tarantool.Interval(year=1, month=2, week=3, day=4, hour=1, ' 'minute=2, sec=3000, nsec=10000000, adjust=Adjust.NONE)', }, + 'min_year_interval': { + 'python': tarantool.Interval(year=-int(MAX_YEAR_RANGE)), + 'msgpack': (b'\x02\x00\xd2\xff\x4c\x91\x8b\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{year=-{int(MAX_YEAR_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{year=-{int(MAX_YEAR_RANGE)} + 1}}) - " + r"datetime.interval.new({year=1})", + 'str': f'tarantool.Interval(year=-{int(MAX_YEAR_RANGE)}, month=0, week=0, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_year_interval': { + 'python': tarantool.Interval(year=int(MAX_YEAR_RANGE)), + 'msgpack': (b'\x02\x00\xce\x00\xb3\x6e\x75\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{year={int(MAX_YEAR_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{year={int(MAX_YEAR_RANGE)} - 1}}) + " + r"datetime.interval.new({year=1})", + 'str': f'tarantool.Interval(year={int(MAX_YEAR_RANGE)}, month=0, week=0, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_month_interval': { + 'python': tarantool.Interval(month=-int(MAX_MONTH_RANGE)), + 'msgpack': (b'\x02\x01\xd2\xf7\x96\xd2\x84\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{month=-{int(MAX_MONTH_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{month=-{int(MAX_MONTH_RANGE)} + 1}}) - " + r"datetime.interval.new({month=1})", + 'str': f'tarantool.Interval(year=0, month=-{int(MAX_MONTH_RANGE)}, week=0, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_month_interval': { + 'python': tarantool.Interval(month=int(MAX_MONTH_RANGE)), + 'msgpack': (b'\x02\x01\xce\x08\x69\x2d\x7c\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{month={int(MAX_MONTH_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{month={int(MAX_MONTH_RANGE)} - 1}}) + " + r"datetime.interval.new({month=1})", + 'str': f'tarantool.Interval(year=0, month={int(MAX_MONTH_RANGE)}, week=0, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_week_interval': { + 'python': tarantool.Interval(week=-int(MAX_WEEK_RANGE)), + 'msgpack': (b'\x02\x02\xd2\xdb\x6d\x85\xa8\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{week=-{int(MAX_WEEK_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{week=-{int(MAX_WEEK_RANGE)} + 1}}) - " + r"datetime.interval.new({week=1})", + 'str': f'tarantool.Interval(year=0, month=0, week=-{int(MAX_WEEK_RANGE)}, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_week_interval': { + 'python': tarantool.Interval(week=int(MAX_WEEK_RANGE)), + 'msgpack': (b'\x02\x02\xce\x24\x92\x7a\x58\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{week={int(MAX_WEEK_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{week={int(MAX_WEEK_RANGE)} - 1}}) + " + r"datetime.interval.new({week=1})", + 'str': f'tarantool.Interval(year=0, month=0, week={int(MAX_WEEK_RANGE)}, day=0, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_day_interval': { + 'python': tarantool.Interval(day=-int(MAX_DAY_RANGE)), + 'msgpack': (b'\x02\x03\xd3\xff\xff\xff\xfe\xff\xfe\xa7\x92\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{day=-{int(MAX_DAY_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{day=-{int(MAX_DAY_RANGE)} + 1}}) - " + r"datetime.interval.new({day=1})", + 'str': f'tarantool.Interval(year=0, month=0, week=0, day=-{int(MAX_DAY_RANGE)}, ' + 'hour=0, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'max_day_interval': { + 'python': tarantool.Interval(day=int(MAX_DAY_RANGE)), + 'msgpack': (b'\x02\x03\xcf\x00\x00\x00\x01\x00\x01\x58\x6e\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{day={int(MAX_DAY_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{day={int(MAX_DAY_RANGE)} - 1}}) + " + r"datetime.interval.new({day=1})", + 'str': f'tarantool.Interval(year=0, month=0, week=0, day={int(MAX_DAY_RANGE)}, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'min_int32_day_interval': { + 'python': tarantool.Interval(day=-2147483648), + 'msgpack': (b'\x02\x03\xd2\x80\x00\x00\x00\x08\x01'), + 'tarantool': r"datetime.interval.new({day=-2147483648})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=-2147483648, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_int32_day_interval': { + 'python': tarantool.Interval(day=2147483647), + 'msgpack': (b'\x02\x03\xce\x7f\xff\xff\xff\x08\x01'), + 'tarantool': r"datetime.interval.new({day=2147483647})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=2147483647, hour=0, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_hour_interval': { + 'python': tarantool.Interval(hour=-int(MAX_HOUR_RANGE)), + 'msgpack': (b'\x02\x04\xd3\xff\xff\xff\xe7\xff\xdf\xb5\xaa\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{hour=-{int(MAX_HOUR_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{hour=-{int(MAX_HOUR_RANGE)} + 1}}) - " + r"datetime.interval.new({hour=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, ' + f'hour=-{int(MAX_HOUR_RANGE)}, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'max_hour_interval': { + 'python': tarantool.Interval(hour=int(MAX_HOUR_RANGE)), + 'msgpack': (b'\x02\x04\xcf\x00\x00\x00\x18\x00\x20\x4a\x56\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{hour={int(MAX_HOUR_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{hour={int(MAX_HOUR_RANGE)} - 1}}) + " + r"datetime.interval.new({hour=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, ' + f'hour={int(MAX_HOUR_RANGE)}, minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'min_int32_hour_interval': { + 'python': tarantool.Interval(hour=-2147483648), + 'msgpack': (b'\x02\x04\xd2\x80\x00\x00\x00\x08\x01'), + 'tarantool': r"datetime.interval.new({hour=-2147483648})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=-2147483648, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_int32_hour_interval': { + 'python': tarantool.Interval(hour=2147483647), + 'msgpack': (b'\x02\x04\xce\x7f\xff\xff\xff\x08\x01'), + 'tarantool': r"datetime.interval.new({hour=2147483647})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=2147483647, ' + 'minute=0, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_minute_interval': { + 'python': tarantool.Interval(minute=-int(MAX_MIN_RANGE)), + 'msgpack': (b'\x02\x05\xd3\xff\xff\xfa\x5f\xf8\x6e\x93\xd8\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{min=-{int(MAX_MIN_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{min=-{int(MAX_MIN_RANGE)} + 1}}) - " + r"datetime.interval.new({min=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute=-{int(MAX_MIN_RANGE)}, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'max_minute_interval': { + 'python': tarantool.Interval(minute=int(MAX_MIN_RANGE)), + 'msgpack': (b'\x02\x05\xcf\x00\x00\x05\xa0\x07\x91\x6c\x28\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{min={int(MAX_MIN_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{min={int(MAX_MIN_RANGE)} - 1}}) + " + r"datetime.interval.new({min=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute={int(MAX_MIN_RANGE)}, sec=0, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'min_int32_minute_interval': { + 'python': tarantool.Interval(minute=-2147483648), + 'msgpack': (b'\x02\x05\xd2\x80\x00\x00\x00\x08\x01'), + 'tarantool': r"datetime.interval.new({min=-2147483648})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=-2147483648, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'max_int32_minute_interval': { + 'python': tarantool.Interval(minute=2147483647), + 'msgpack': (b'\x02\x05\xce\x7f\xff\xff\xff\x08\x01'), + 'tarantool': r"datetime.interval.new({min=2147483647})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=2147483647, sec=0, nsec=0, adjust=Adjust.NONE)', + }, + 'min_sec_interval': { + 'python': tarantool.Interval(sec=-int(MAX_SEC_RANGE)), + 'msgpack': (b'\x02\x06\xd3\xff\xfe\xae\x7e\x39\xea\xa6\xa0\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{sec=-{int(MAX_SEC_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{sec=-{int(MAX_SEC_RANGE)} + 1}}) - " + r"datetime.interval.new({sec=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute=0, sec=-{int(MAX_SEC_RANGE)}, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'max_sec_interval': { + 'python': tarantool.Interval(sec=int(MAX_SEC_RANGE)), + 'msgpack': (b'\x02\x06\xcf\x00\x01\x51\x81\xc6\x15\x59\x60\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{sec={int(MAX_SEC_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{sec={int(MAX_SEC_RANGE)} - 1}}) + " + r"datetime.interval.new({sec=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute=0, sec={int(MAX_SEC_RANGE)}, nsec=0, adjust=Adjust.NONE)', + 'tarantool_8887_issue': True, + }, + 'min_int32_sec_interval': { + 'python': tarantool.Interval(sec=-2147483648), + 'msgpack': (b'\x02\x06\xd2\x80\x00\x00\x00\x08\x01'), + 'tarantool': r"datetime.interval.new({sec=-2147483648})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=-2147483648, nsec=0, adjust=Adjust.NONE)', + }, + 'max_int32_sec_interval': { + 'python': tarantool.Interval(sec=2147483647), + 'msgpack': (b'\x02\x06\xce\x7f\xff\xff\xff\x08\x01'), + 'tarantool': r"datetime.interval.new({sec=2147483647})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + 'minute=0, sec=2147483647, nsec=0, adjust=Adjust.NONE)', + }, + 'min_nsec_interval': { + 'python': tarantool.Interval(nsec=-int(MAX_NSEC_RANGE)), + 'msgpack': (b'\x02\x07\xd2\x80\x00\x00\x01\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{nsec=-{int(MAX_NSEC_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{nsec=-{int(MAX_NSEC_RANGE)} + 1}}) - " + r"datetime.interval.new({nsec=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute=0, sec=0, nsec=-{int(MAX_NSEC_RANGE)}, adjust=Adjust.NONE)', + }, + 'max_nsec_interval': { + 'python': tarantool.Interval(nsec=int(MAX_NSEC_RANGE)), + 'msgpack': (b'\x02\x07\xce\x7f\xff\xff\xff\x08\x01'), + # Reason why Tarantool datetime is so weird here: + # https://github.com/tarantool/tarantool/issues/8878 + # Replace with f"datetime.interval.new({{nsec={int(MAX_NSEC_RANGE)}}}) + # after fix. + 'tarantool': f"datetime.interval.new({{nsec={int(MAX_NSEC_RANGE)} - 1}}) + " + r"datetime.interval.new({nsec=1})", + 'str': 'tarantool.Interval(year=0, month=0, week=0, day=0, hour=0, ' + f'minute=0, sec=0, nsec={int(MAX_NSEC_RANGE)}, adjust=Adjust.NONE)', + }, } def test_msgpack_decode(self): @@ -215,6 +481,9 @@ def test_msgpack_decode(self): def test_tarantool_decode(self): for name, case in self.cases.items(): with self.subTest(msg=name): + if ('tarantool_8887_issue' in case) and (case['tarantool_8887_issue'] is True): + self.skipTest('See https://github.com/tarantool/tarantool/issues/8887') + self.adm(f"box.space['test']:replace{{'{name}', {case['tarantool']}, 'field'}}") self.assertSequenceEqual(self.con.select('test', name), @@ -230,6 +499,9 @@ def test_msgpack_encode(self): def test_tarantool_encode(self): for name, case in self.cases.items(): with self.subTest(msg=name): + if ('tarantool_8887_issue' in case) and (case['tarantool_8887_issue'] is True): + self.skipTest('See https://github.com/tarantool/tarantool/issues/8887') + self.con.insert('test', [name, case['python'], 'field']) lua_eval = f""" @@ -266,6 +538,87 @@ def test_unknown_adjust_decode(self): MsgpackError, '3 is not a valid Adjust', lambda: unpacker_ext_hook(6, case, self.con._unpacker_factory())) + out_of_range_cases = { + 'year_too_small': { + 'kwargs': {'year': -int(MAX_YEAR_RANGE + 1)}, + 'range': MAX_YEAR_RANGE, + }, + 'year_too_large': { + 'kwargs': {'year': int(MAX_YEAR_RANGE + 1)}, + 'range': MAX_YEAR_RANGE, + }, + 'month_too_small': { + 'kwargs': {'month': -int(MAX_MONTH_RANGE + 1)}, + 'range': MAX_MONTH_RANGE, + }, + 'month_too_big': { + 'kwargs': {'month': int(MAX_MONTH_RANGE + 1)}, + 'range': MAX_MONTH_RANGE, + }, + 'week_too_small': { + 'kwargs': {'week': -int(MAX_WEEK_RANGE + 1)}, + 'range': MAX_WEEK_RANGE, + }, + 'week_too_big': { + 'kwargs': {'week': int(MAX_WEEK_RANGE + 1)}, + 'range': MAX_WEEK_RANGE, + }, + 'day_too_small': { + 'kwargs': {'day': -int(MAX_DAY_RANGE + 1)}, + 'range': MAX_DAY_RANGE, + }, + 'day_too_big': { + 'kwargs': {'day': int(MAX_DAY_RANGE + 1)}, + 'range': MAX_DAY_RANGE, + }, + 'hour_too_small': { + 'kwargs': {'hour': -int(MAX_HOUR_RANGE + 1)}, + 'range': MAX_HOUR_RANGE, + }, + 'hour_too_big': { + 'kwargs': {'hour': int(MAX_HOUR_RANGE + 1)}, + 'range': MAX_HOUR_RANGE, + }, + 'minute_too_small': { + 'kwargs': {'minute': -int(MAX_MIN_RANGE + 1)}, + 'range': MAX_MIN_RANGE, + }, + 'minute_too_big': { + 'kwargs': {'minute': int(MAX_MIN_RANGE + 1)}, + 'range': MAX_MIN_RANGE, + }, + 'sec_too_small': { + 'kwargs': {'sec': -int(MAX_SEC_RANGE + 1)}, + 'range': MAX_SEC_RANGE, + }, + 'sec_too_big': { + 'kwargs': {'sec': int(MAX_SEC_RANGE + 1)}, + 'range': MAX_SEC_RANGE, + }, + 'nsec_too_small': { + 'kwargs': {'nsec': -int(MAX_NSEC_RANGE + 1)}, + 'range': MAX_NSEC_RANGE, + }, + 'nsec_too_big': { + 'kwargs': {'nsec': int(MAX_NSEC_RANGE + 1)}, + 'range': MAX_NSEC_RANGE, + }, + } + + def test_out_of_range(self): + # pylint: disable=cell-var-from-loop + + for name, case in self.out_of_range_cases.items(): + with self.subTest(msg=name): + name = next(iter(case['kwargs'])) + val = case['kwargs'][name] + self.assertRaisesRegex( + ValueError, re.escape( + f"value {val} of {name} is out of " + f"allowed range [{-case['range']}, {case['range']}]" + ), + lambda: tarantool.Interval(**case['kwargs'])) + arithmetic_cases = { 'year': { 'arg_1': tarantool.Interval(year=2), @@ -369,6 +722,22 @@ def test_tarantool_interval_subtraction(self): self.assertSequenceEqual(self.con.call('sub', case['arg_1'], case['arg_2']), [case['res_sub']]) + def test_addition_overflow(self): + self.assertRaisesRegex( + ValueError, re.escape( + f"value {int(MAX_YEAR_RANGE) + 1} of year is out of " + f"allowed range [{-MAX_YEAR_RANGE}, {MAX_YEAR_RANGE}]" + ), + lambda: tarantool.Interval(year=int(MAX_YEAR_RANGE)) + tarantool.Interval(year=1)) + + def test_subtraction_overflow(self): + self.assertRaisesRegex( + ValueError, re.escape( + f"value {-int(MAX_YEAR_RANGE) - 1} of year is out of " + f"allowed range [{-MAX_YEAR_RANGE}, {MAX_YEAR_RANGE}]" + ), + lambda: tarantool.Interval(year=-int(MAX_YEAR_RANGE)) - tarantool.Interval(year=1)) + @classmethod def tearDownClass(cls): cls.con.close() From 3a8e39fd826c47fb6245ce1054fddd50ee3d0ea9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 18 Jul 2023 10:20:47 +0300 Subject: [PATCH 158/190] changelog: fix spelling --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e9aff0e..19ea2b26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,7 +207,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support pandas way to build datetime from timestamp (PR #252). `timestamp_since_utc_epoch` is a parameter to set timestamp - convertion behavior for timezone-aware datetimes. + conversion behavior for timezone-aware datetimes. If ``False`` (default), behaves similar to Tarantool `datetime.new()`: @@ -227,7 +227,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Thus, if ``False``, datetime is computed from timestamp since epoch and then timezone is applied without any - convertion. In that case, `dt.timestamp` won't be equal to + conversion. In that case, `dt.timestamp` won't be equal to initialization `timestamp` for all timezones with non-zero offset. If ``True``, behaves similar to `pandas.Timestamp`: From f2104eef46ea9277e8a1032daaae59f21eab28b7 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 18 Jul 2023 10:18:51 +0300 Subject: [PATCH 159/190] lint: validate changelog with codespell --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 25809cd8..d3919795 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ install: PYTHON_FILES=tarantool test setup.py docs/source/conf.py -TEXT_FILES=README.rst docs/source/*.rst +TEXT_FILES=README.rst CHANGELOG.md docs/source/*.rst .PHONY: lint lint: python3 -m pylint --recursive=y $(PYTHON_FILES) From 21f77a6bd23741bdb90dea8d0aed315af45b5e0a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 19 Jul 2023 17:21:22 +0300 Subject: [PATCH 160/190] release 1.1.1 Overview This release introduces various datetime interval fixes and quality of life improvements. Breaking changes - Forbid to create datetime intervals out of Tarantool limits (PR #302). Changed - Validate `tarantool.Interval` limits with the same rules as in Tarantool (PR #302). Fixed - `tarantool.Interval` arithmetic with weeks (PR #302). - `tarantool.Interval` weeks display in `str()` and `repr()` (PR #302). --- CHANGELOG.md | 8 ++++---- debian/changelog | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ea2b26..13e58a2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,14 @@ All notable changes to this project will be documented in this file. 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 +## 1.1.1 - 2023-07-19 ### Changed -- Validate `tarantool.Interval` limits with the same rules as in Tarantool. +- Validate `tarantool.Interval` limits with the same rules as in Tarantool (PR #302). ### Fixed -- `tarantool.Interval` arithmetic with weeks -- `tarantool.Interval` weeks display in `str()` and `repr()` +- `tarantool.Interval` arithmetic with weeks (PR #302). +- `tarantool.Interval` weeks display in `str()` and `repr()` (PR #302). ## 1.1.0 - 2023-06-30 diff --git a/debian/changelog b/debian/changelog index 3d653acf..429c65a1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,22 @@ +python3-tarantool (1.1.1-0) unstable; urgency=medium + + ## Overview + + This release introduces various datetime interval fixes and quality of life + improvements. + + ## Breaking changes + - Forbid to create datetime intervals out of Tarantool limits (PR #302). + + ## Changed + - Validate `tarantool.Interval` limits with the same rules as in Tarantool (PR #302). + + ## Fixed + - `tarantool.Interval` arithmetic with weeks (PR #302). + - `tarantool.Interval` weeks display in `str()` and `repr()` (PR #302). + + -- Georgy Moiseev Wed, 19 Jul 2023 18:00:00 +0300 + python3-tarantool (1.1.0-0) unstable; urgency=medium ## Overview From a64c6e0b5e7b803f30d5b074c30edeba75814913 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 7 Aug 2023 17:41:00 +0300 Subject: [PATCH 161/190] test: wait until server is ready Current `wait_until_started` method wait till `box.info.status` is "running" and start sending requests. Such approach is wrong: status "running" [1] does not guarantees that instance is available for writing (like creating new users) [2]. It results in flaky test results (for example, see 4 restarts in [3]). This patch fixes the issue. After this patch, it is guaranteed that after `wait_until_ready` call instance had finished to run its initialization script, including box.cfg, and available for writing data if it is expected to be able to. Windows instances use a separate lock mechanism, so this patch is not related to Windows instances. 1. https://github.com/tarantool/tarantool/blob/983a7ec215d46b6d02935d1baa8bbe07fc371795/src/box/box.cc#L5425-L5426 2. https://github.com/tarantool/tarantool/blob/983a7ec215d46b6d02935d1baa8bbe07fc371795/src/box/box.cc#L5454 3. https://github.com/tarantool/tarantool/actions/runs/5759802620 --- test/suites/box.lua | 2 ++ test/suites/crud_server.lua | 2 ++ test/suites/lib/tarantool_server.py | 21 ++++++++------------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/test/suites/box.lua b/test/suites/box.lua index eb4330dd..c8110d18 100644 --- a/test/suites/box.lua +++ b/test/suites/box.lua @@ -19,3 +19,5 @@ box.cfg{ pid_file = "box.pid", auth_type = (auth_type:len() > 0) and auth_type or nil, } + +rawset(_G, 'ready', true) diff --git a/test/suites/crud_server.lua b/test/suites/crud_server.lua index 8bb58fcb..f939818a 100644 --- a/test/suites/crud_server.lua +++ b/test/suites/crud_server.lua @@ -91,3 +91,5 @@ if crud_imported == false or vshard_imported == false then else configure_crud_instance(primary_listen, crud, vshard) end + +rawset(_G, 'ready', true) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 1dd5e053..c8f34b37 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -302,27 +302,22 @@ def prepare_args(self): return shlex.split(self.binary if not self.script else self.script_dst) - def wait_until_started(self): + def wait_until_ready(self): """ - Wait until server is started. + Wait until server is configured and ready to work. Server consists of two parts: 1) wait until server is listening on sockets - 2) wait until server tells us his status + 2) wait until server finishes executing its script """ while True: try: temp = TarantoolAdmin('0.0.0.0', self.args['admin']) - while True: - ans = temp('box.info.status')[0] - if ans in ('running', 'hot_standby', 'orphan') or ans.startswith('replica'): - temp.disconnect() - return True - if ans in ('loading',): - continue - - raise ValueError(f"Strange output for `box.info.status`: {ans}") + ans = temp('ready')[0] + temp.disconnect() + if isinstance(ans, bool) and ans: + return True except socket.error as exc: if exc.errno == errno.ECONNREFUSED: time.sleep(0.1) @@ -352,7 +347,7 @@ def start(self): cwd=self.vardir, stdout=self.log_des, stderr=self.log_des) - self.wait_until_started() + self.wait_until_ready() def stop(self): """ From ed8d3d2673cf2cdbba2add458cb1d7827a956085 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 7 Aug 2023 17:42:34 +0300 Subject: [PATCH 162/190] ci: use WSL with Ubuntu 22.04 Some installation certificates are outdated for Ubuntu 22.04. --- .github/workflows/packing.yml | 8 ++++---- .github/workflows/testing.yml | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index eabf5096..b5b84f32 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -148,16 +148,16 @@ jobs: - name: Setup WSL for tarantool uses: Vampire/setup-wsl@v1 with: - distribution: Ubuntu-20.04 + distribution: Ubuntu-22.04 - name: Install tarantool - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | curl -L https://tarantool.io/release/2/installer.sh | bash -s sudo apt install -y tarantool tarantool-dev - name: Setup test tarantool instance - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | rm -f ./tarantool.pid ./tarantool.log TNT_PID=$(tarantool ./test/suites/lib/tarantool_python_ci.lua > tarantool.log 2>&1 & echo $!) @@ -172,7 +172,7 @@ jobs: - name: Stop test tarantool instance if: ${{ always() }} - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | cat tarantool.log || true kill $(cat tarantool.pid) || true diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 199744f2..88fa08d7 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -262,16 +262,16 @@ jobs: - name: Setup WSL for tarantool uses: Vampire/setup-wsl@v2 with: - distribution: Ubuntu-20.04 + distribution: Ubuntu-22.04 - name: Install tarantool ${{ matrix.tarantool }} for WSL (2.10 and newer) - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | curl -L https://tarantool.io/release/2/installer.sh | bash -s sudo apt install -y tarantool=${{ matrix.tarantool }} tarantool-dev=${{ matrix.tarantool }} - name: Setup test tarantool instance - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | rm -f ./tarantool.pid ./tarantool.log TNT_PID=$(tarantool ./test/suites/lib/tarantool_python_ci.lua > tarantool.log 2>&1 & echo $!) @@ -286,7 +286,7 @@ jobs: - name: Stop test tarantool instance if: ${{ always() }} - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | cat tarantool.log || true kill $(cat tarantool.pid) || true @@ -334,16 +334,16 @@ jobs: - name: Setup WSL for tarantool uses: Vampire/setup-wsl@v2 with: - distribution: Ubuntu-20.04 + distribution: Ubuntu-22.04 - name: Install tarantool ${{ matrix.tarantool }} for WSL - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | curl -L https://tarantool.io/release/2/installer.sh | bash -s sudo apt install -y tarantool=${{ matrix.tarantool }} tarantool-dev=${{ matrix.tarantool }} - name: Setup test tarantool instance - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | rm -f ./tarantool.pid ./tarantool.log TNT_PID=$(tarantool ./test/suites/lib/tarantool_python_ci.lua > tarantool.log 2>&1 & echo $!) @@ -358,7 +358,7 @@ jobs: - name: Stop test tarantool instance if: ${{ always() }} - shell: wsl-bash_Ubuntu-20.04 {0} + shell: wsl-bash_Ubuntu-22.04 {0} run: | cat tarantool.log || true kill $(cat tarantool.pid) || true From 42bbe146e8cf3e7e4162f87324ec350a5d33b526 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 7 Aug 2023 19:24:25 +0300 Subject: [PATCH 163/190] test: assert admin commands success Admin command are sent through text protocol. Responses are rarely processed, especially those which sets up schema and users. But such requests can contain errors, like "already exists" or syntax ones. Having asserts on schema set up makes it easier to debug some cases. For example, it was helpful in discovering the reason behind "user not exists" fails fixed in "wait until server is ready" patchset commit [1]. 1. https://github.com/tarantool/tarantool-python/actions/runs/5784619729/job/15675655683?pr=306 --- test/suites/test_connection.py | 71 +++++++++++++-------- test/suites/test_datetime.py | 23 +++++-- test/suites/test_dbapi.py | 13 ++-- test/suites/test_decimal.py | 29 ++++++--- test/suites/test_dml.py | 62 ++++++++++-------- test/suites/test_encoding.py | 84 +++++++++++++----------- test/suites/test_error_ext.py | 20 ++++-- test/suites/test_execute.py | 13 ++-- test/suites/test_interval.py | 20 ++++-- test/suites/test_mesh.py | 29 ++++++--- test/suites/test_pool.py | 60 ++++++++++------- test/suites/test_push.py | 94 ++++++++++++--------------- test/suites/test_schema.py | 113 ++++++++++++++++++--------------- test/suites/test_uuid.py | 39 ++++++++---- test/suites/utils.py | 16 +++++ 15 files changed, 419 insertions(+), 267 deletions(-) create mode 100644 test/suites/utils.py diff --git a/test/suites/test_connection.py b/test/suites/test_connection.py index 52234608..4402f0b0 100644 --- a/test/suites/test_connection.py +++ b/test/suites/test_connection.py @@ -5,8 +5,9 @@ import sys import unittest - import decimal + +import pkg_resources import msgpack import tarantool @@ -14,6 +15,7 @@ from .lib.skip import skip_or_run_decimal_test, skip_or_run_varbinary_test from .lib.tarantool_server import TarantoolServer +from .utils import assert_admin_success class TestSuiteConnection(unittest.TestCase): @@ -26,35 +28,48 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" + resp = cls.adm(""" box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute', 'universe') - - box.schema.create_space('space_varbin') - - box.space['space_varbin']:format({ - { - 'id', - type = 'number', - is_nullable = false - }, - { - 'varbin', - type = 'varbinary', - is_nullable = false, - } - }) - - box.space['space_varbin']:create_index('id', { - type = 'tree', - parts = {1, 'number'}, - unique = true}) - - box.space['space_varbin']:create_index('varbin', { - type = 'tree', - parts = {2, 'varbinary'}, - unique = true}) + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) + + return true """) + assert_admin_success(resp) + + if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.2.1'): + resp = cls.adm(""" + box.schema.create_space('space_varbin', {if_not_exists = true}) + + box.space['space_varbin']:format({ + { + 'id', + type = 'number', + is_nullable = false + }, + { + 'varbin', + type = 'varbinary', + is_nullable = false, + } + }) + + box.space['space_varbin']:create_index('id', { + type = 'tree', + parts = {1, 'number'}, + unique = true, + if_not_exists = true}) + + box.space['space_varbin']:create_index('varbin', { + type = 'tree', + parts = {2, 'varbinary'}, + unique = true, + if_not_exists = true}) + + return true + """) + assert_admin_success(resp) + cls.con = None def setUp(self): diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index ae22dcfc..9f250804 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -16,6 +16,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test, skip_or_run_datetime_2_11_test +from .utils import assert_admin_success class TestSuiteDatetime(unittest.TestCase): @@ -28,25 +29,28 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" + resp = cls.adm(""" _, datetime = pcall(require, 'datetime') - box.schema.space.create('test') + box.schema.space.create('test', {if_not_exists = true}) box.space['test']:create_index('primary', { type = 'tree', parts = {1, 'string'}, - unique = true}) + unique = true, + if_not_exists = true}) pcall(function() box.schema.space.create('test_pk') box.space['test_pk']:create_index('primary', { type = 'tree', parts = {1, 'datetime'}, - unique = true}) + unique = true, + if_not_exists = true}) end) box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute', 'universe') + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) local function add(arg1, arg2) return arg1 + arg2 @@ -57,7 +61,10 @@ def setUpClass(cls): return arg1 - arg2 end rawset(_G, 'sub', sub) + + return true """) + assert_admin_success(resp) cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], user='test', password='test') @@ -67,7 +74,11 @@ def setUp(self): if self.srv.is_started(): self.srv.touch_lock() - self.adm("box.space['test']:truncate()") + resp = self.adm(""" + box.space['test']:truncate() + return true + """) + assert_admin_success(resp) def test_datetime_class_api(self): datetime = tarantool.Datetime(year=2022, month=8, day=31, hour=18, minute=7, sec=54, diff --git a/test/suites/test_dbapi.py b/test/suites/test_dbapi.py index 83156b39..bd8c7487 100644 --- a/test/suites/test_dbapi.py +++ b/test/suites/test_dbapi.py @@ -12,6 +12,7 @@ from tarantool import dbapi from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_sql_test +from .utils import assert_admin_success class TestSuiteDBAPI(dbapi20.DatabaseAPI20Test): @@ -39,6 +40,14 @@ def setUpClass(cls): "port": cls.srv.args['primary'] } + # grant full access to guest + resp = cls.srv.admin(""" + box.schema.user.grant('guest', 'create,read,write,execute', 'universe', + nil, {if_not_exists = true}) + return true + """) + assert_admin_success(resp) + @skip_or_run_sql_test def setUp(self): # prevent a remote tarantool from clean our session @@ -46,10 +55,6 @@ def setUp(self): self.srv.touch_lock() self.con.flush_schema() - # grant full access to guest - self.srv.admin("box.schema.user.grant('guest', 'create,read,write," - "execute', 'universe')") - @classmethod def tearDownClass(cls): cls.con.close() diff --git a/test/suites/test_decimal.py b/test/suites/test_decimal.py index b880eaa3..2875a7da 100644 --- a/test/suites/test_decimal.py +++ b/test/suites/test_decimal.py @@ -16,6 +16,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_decimal_test +from .utils import assert_admin_success class TestSuiteDecimal(unittest.TestCase): @@ -28,26 +29,32 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" - _, decimal = pcall(require, 'decimal') + resp = cls.adm(""" + decimal_supported, decimal = pcall(require, 'decimal') - box.schema.space.create('test') + box.schema.space.create('test', {if_not_exists = true}) box.space['test']:create_index('primary', { type = 'tree', parts = {1, 'string'}, - unique = true}) + unique = true, + if_not_exists = true}) - pcall(function() + if decimal_supported then box.schema.space.create('test_pk') box.space['test_pk']:create_index('primary', { type = 'tree', parts = {1, 'decimal'}, - unique = true}) - end) + unique = true, + if_not_exists = true}) + end box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute', 'universe') + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) + + return true """) + assert_admin_success(resp) cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], user='test', password='test') @@ -57,7 +64,11 @@ def setUp(self): if self.srv.is_started(): self.srv.touch_lock() - self.adm("box.space['test']:truncate()") + resp = self.adm(""" + box.space['test']:truncate() + return true + """) + assert_admin_success(resp) valid_cases = { 'simple_decimal_1': { diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 69293b55..f557451e 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -10,6 +10,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_extra_info_test +from .utils import assert_admin_success class TestSuiteRequest(unittest.TestCase): @@ -22,29 +23,35 @@ def setUpClass(cls): cls.srv.start() cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) cls.adm = cls.srv.admin - cls.space_created = cls.adm("box.schema.create_space('space_1')") - cls.adm(""" - box.space['space_1']:create_index('primary', { - type = 'tree', - parts = {1, 'num'}, - unique = true}) - """.replace('\n', ' ')) - cls.adm(""" - box.space['space_1']:create_index('secondary', { - type = 'tree', - parts = {2, 'num', 3, 'str'}, - unique = false}) - """.replace('\n', ' ')) - cls.space_created = cls.adm("box.schema.create_space('space_2')") - cls.adm(""" - box.space['space_2']:create_index('primary', { - type = 'hash', - parts = {1, 'num'}, - unique = true}) - """.replace('\n', ' ')) - cls.adm("json = require('json')") - cls.adm("fiber = require('fiber')") - cls.adm("uuid = require('uuid')") + cls.space_created = cls.adm("box.schema.create_space('space_1', {if_not_exists = true})") + resp = cls.adm(""" + box.space['space_1']:create_index('primary', { + type = 'tree', + parts = {1, 'num'}, + unique = true, + if_not_exists = true}) + + box.space['space_1']:create_index('secondary', { + type = 'tree', + parts = {2, 'num', 3, 'str'}, + unique = false, + if_not_exists = true}) + + box.schema.create_space('space_2', {if_not_exists = true}) + + box.space['space_2']:create_index('primary', { + type = 'hash', + parts = {1, 'num'}, + unique = true, + if_not_exists = true}) + + json = require('json') + fiber = require('fiber') + uuid = require('uuid') + + return true + """) + assert_admin_success(resp) if not sys.platform.startswith("win"): cls.sock_srv = TarantoolServer(create_unix_socket=True) @@ -60,10 +67,11 @@ def setUp(self): def test_00_00_authenticate(self): self.assertIsNone(self.srv.admin(""" - box.schema.user.create('test', { password = 'test' }) + box.schema.user.create('test', { password = 'test', if_not_exists = true }) """)) self.assertIsNone(self.srv.admin(""" - box.schema.user.grant('test', 'execute,read,write', 'universe') + box.schema.user.grant('test', 'execute,read,write', 'universe', + nil, {if_not_exists = true}) """)) self.assertEqual(self.con.authenticate('test', 'test')._data, None) @@ -311,7 +319,7 @@ def test_11_select_all_hash(self): space.select((), iterator=tarantool.const.ITERATOR_EQ) def test_12_update_fields(self): - self.srv.admin( + resp = self.srv.admin( """ do local sp = box.schema.create_space('sp', { @@ -325,7 +333,9 @@ def test_12_update_fields(self): parts = {1, 'unsigned'} }) end + return true """) + assert_admin_success(resp) self.con.insert('sp', [2, 'help', 4]) self.assertSequenceEqual( self.con.update('sp', (2,), [('+', 'thi', 3)]), diff --git a/test/suites/test_encoding.py b/test/suites/test_encoding.py index 14a08f54..dcda5983 100644 --- a/test/suites/test_encoding.py +++ b/test/suites/test_encoding.py @@ -1,16 +1,18 @@ """ This module tests various type encoding cases. """ -# pylint: disable=missing-class-docstring,missing-function-docstring +# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code import sys import unittest +import pkg_resources import tarantool from tarantool.error import DatabaseError from .lib.skip import skip_or_run_varbinary_test, skip_or_run_error_extra_info_test from .lib.tarantool_server import TarantoolServer +from .utils import assert_admin_success class TestSuiteEncoding(unittest.TestCase): @@ -24,10 +26,14 @@ def setUpClass(cls): cls.srv.script = 'test/suites/box.lua' cls.srv.start() - cls.srv.admin(""" - box.schema.user.create('test', { password = 'test' }) - box.schema.user.grant('test', 'execute,read,write', 'universe') + resp = cls.srv.admin(""" + box.schema.user.create('test', { password = 'test', if_not_exists = true }) + box.schema.user.grant('test', 'execute,read,write', 'universe', + nil, {if_not_exists = true}) + + return true """) + assert_admin_success(resp) args = [cls.srv.host, cls.srv.args['primary']] kwargs = {'user': 'test', 'password': 'test'} @@ -35,41 +41,47 @@ def setUpClass(cls): cls.con_encoding_none = tarantool.Connection(*args, encoding=None, **kwargs) cls.conns = [cls.con_encoding_utf8, cls.con_encoding_none] - cls.srv.admin("box.schema.create_space('space_str')") - cls.srv.admin(""" + resp = cls.srv.admin(""" + box.schema.create_space('space_str', {if_not_exists = true}) box.space['space_str']:create_index('primary', { type = 'tree', parts = {1, 'str'}, - unique = true}) - """.replace('\n', ' ')) - - cls.srv.admin("box.schema.create_space('space_varbin')") - cls.srv.admin(r""" - box.space['space_varbin']:format({ - { - 'id', - type = 'number', - is_nullable = false - }, - { - 'varbin', - type = 'varbinary', - is_nullable = false, - } - }) - """.replace('\n', ' ')) - cls.srv.admin(""" - box.space['space_varbin']:create_index('id', { - type = 'tree', - parts = {1, 'number'}, - unique = true}) - """.replace('\n', ' ')) - cls.srv.admin(""" - box.space['space_varbin']:create_index('varbin', { - type = 'tree', - parts = {2, 'varbinary'}, - unique = true}) - """.replace('\n', ' ')) + unique = true, + if_not_exists = true}) + + return true + """) + assert_admin_success(resp) + + if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.2.1'): + resp = cls.srv.admin(""" + box.schema.create_space('space_varbin', {if_not_exists = true}) + box.space['space_varbin']:format({ + { + 'id', + type = 'number', + is_nullable = false + }, + { + 'varbin', + type = 'varbinary', + is_nullable = false, + } + }) + box.space['space_varbin']:create_index('id', { + type = 'tree', + parts = {1, 'number'}, + unique = true, + if_not_exists = true}) + box.space['space_varbin']:create_index('varbin', { + type = 'tree', + parts = {2, 'varbinary'}, + unique = true, + if_not_exists = true}) + + return true + """) + assert_admin_success(resp) def assertNotRaises(self, func, *args, **kwargs): try: diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 8d1a63cf..52622794 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -15,6 +15,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_error_ext_type_test +from .utils import assert_admin_success class TestSuiteErrorExt(unittest.TestCase): @@ -27,18 +28,23 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" - box.schema.space.create('test') + resp = cls.adm(""" + box.schema.space.create('test', {if_not_exists = true}) box.space['test']:create_index('primary', { type = 'tree', parts = {1, 'string'}, - unique = true}) + unique = true, + if_not_exists = true}) box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute,create', 'universe') + box.schema.user.grant('test', 'read,write,execute,create', 'universe', + nil, {if_not_exists = true}) box.schema.user.create('no_grants', {if_not_exists = true}) + + return true """) + assert_admin_success(resp) cls.conn_encoding_utf8 = tarantool.Connection( cls.srv.host, cls.srv.args['primary'], @@ -78,7 +84,11 @@ def setUp(self): if self.srv.is_started(): self.srv.touch_lock() - self.adm("box.space['test']:truncate()") + resp = self.adm(""" + box.space['test']:truncate() + return true + """) + assert_admin_success(resp) # msgpack data for different encodings are actually the same, # but sometimes python msgpack module use different string diff --git a/test/suites/test_execute.py b/test/suites/test_execute.py index 66fe0982..46e85ce7 100644 --- a/test/suites/test_execute.py +++ b/test/suites/test_execute.py @@ -9,6 +9,7 @@ import tarantool from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_sql_test +from .utils import assert_admin_success class TestSuiteExecute(unittest.TestCase): @@ -32,6 +33,14 @@ def setUpClass(cls): cls.srv.start() cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary']) + # grant full access to guest + resp = cls.srv.admin(""" + box.schema.user.grant('guest', 'create,read,write,execute', 'universe', + nil, {if_not_exists = true}) + return true + """) + assert_admin_success(resp) + @skip_or_run_sql_test def setUp(self): # prevent a remote tarantool from clean our session @@ -39,10 +48,6 @@ def setUp(self): self.srv.touch_lock() self.con.flush_schema() - # grant full access to guest - self.srv.admin("box.schema.user.grant('guest', 'create,read,write," - "execute', 'universe')") - @classmethod def tearDownClass(cls): cls.con.close() diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index b2726776..c241b312 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -26,6 +26,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_datetime_test +from .utils import assert_admin_success class TestSuiteInterval(unittest.TestCase): @@ -38,17 +39,19 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" + resp = cls.adm(""" _, datetime = pcall(require, 'datetime') - box.schema.space.create('test') + box.schema.space.create('test', {if_not_exists = true}) box.space['test']:create_index('primary', { type = 'tree', parts = {1, 'string'}, - unique = true}) + unique = true, + if_not_exists = true}) box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute', 'universe') + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) local function add(arg1, arg2) return arg1 + arg2 @@ -59,7 +62,10 @@ def setUpClass(cls): return arg1 - arg2 end rawset(_G, 'sub', sub) + + return true """) + assert_admin_success(resp) cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], user='test', password='test') @@ -69,7 +75,11 @@ def setUp(self): if self.srv.is_started(): self.srv.touch_lock() - self.adm("box.space['test']:truncate()") + resp = self.adm(""" + box.space['test']:truncate() + return true + """) + assert_admin_success(resp) def test_interval_positional_init(self): self.assertRaisesRegex( diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index b82ccc0e..606cc7d1 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -16,18 +16,23 @@ ClusterDiscoveryWarning, ) from .lib.tarantool_server import TarantoolServer +from .utils import assert_admin_success def create_server(_id): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " - "if_not_exists = true})") - srv.admin("box.schema.user.grant('test', 'execute', 'universe')") + resp = srv.admin(f""" + box.schema.user.create('test', {{password = 'test', if_not_exists = true}}) + box.schema.user.grant('test', 'execute', 'universe', + nil, {{if_not_exists = true}}) - # Create srv_id function (for testing purposes). - srv.admin(f"function srv_id() return {_id} end") + function srv_id() return {_id} end + + return true + """) + assert_admin_success(resp) return srv @@ -43,18 +48,22 @@ def define_cluster_function(self, func_name, servers): function {func_name}() return {{{addresses_lua}}} end + return true """ for srv in self.servers: - srv.admin(func_body) + resp = srv.admin(func_body) + assert_admin_success(resp) def define_custom_cluster_function(self, func_name, retval): func_body = f""" function {func_name}() return {retval} end + return true """ for srv in self.servers: - srv.admin(func_body) + resp = srv.admin(func_body) + assert_admin_success(resp) @classmethod def setUpClass(cls): @@ -99,7 +108,11 @@ def assert_srv_id(con, srv_id): # Start instance#1, stop instance#2 -- response from # instance#1 again. self.srv.start() - self.srv.admin('function srv_id() return 1 end') + resp = self.srv.admin(""" + function srv_id() return 1 end + return true + """) + assert_admin_success(resp) self.srv2.stop() assert_srv_id(con, 1) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 7134f220..e15bcbbf 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -20,31 +20,46 @@ from .lib.skip import skip_or_run_sql_test from .lib.tarantool_server import TarantoolServer +from .utils import assert_admin_success def create_server(_id): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " - "if_not_exists = true})") - srv.admin("box.schema.user.grant('test', 'execute', 'universe')") - srv.admin("box.schema.space.create('test')") - srv.admin(r"box.space.test:format({" - r" { name = 'pk', type = 'string' }," - r" { name = 'id', type = 'number', is_nullable = true }" - r"})") - srv.admin(r"box.space.test:create_index('pk'," - r"{ unique = true," - r" parts = {{field = 1, type = 'string'}}})") - srv.admin(r"box.space.test:create_index('id'," - r"{ unique = true," - r" parts = {{field = 2, type = 'number', is_nullable=true}}})") - srv.admin("box.schema.user.grant('test', 'read,write', 'space', 'test')") - srv.admin("json = require('json')") - - # Create srv_id function (for testing purposes). - srv.admin(f"function srv_id() return {_id} end") + resp = srv.admin(f""" + box.schema.user.create('test', {{password = 'test', if_not_exists = true}}) + box.schema.user.grant('test', 'execute', 'universe', + nil, {{if_not_exists = true}}) + box.schema.space.create('test', {{if_not_exists = true}}) + box.space.test:format({{ + {{ name = 'pk', type = 'string' }}, + {{ name = 'id', type = 'number', is_nullable = true }} + }}) + box.space.test:create_index('pk', + {{ + unique = true, + parts = {{ + {{field = 1, type = 'string'}} + }}, + if_not_exists = true, + }}) + box.space.test:create_index('id', + {{ + unique = true, + parts = {{ + {{field = 2, type = 'number', is_nullable=true}} + }}, + if_not_exists = true, + }}) + box.schema.user.grant('test', 'read,write', 'space', 'test', {{if_not_exists = true}}) + json = require('json') + + function srv_id() return {_id} end + + return true + """) + assert_admin_success(resp) return srv @@ -53,11 +68,12 @@ def create_server(_id): class TestSuitePool(unittest.TestCase): def set_ro(self, srv, read_only): if read_only: - req = r'box.cfg{read_only = true}' + req = r'box.cfg{read_only = true}; return true' else: - req = r'box.cfg{read_only = false}' + req = r'box.cfg{read_only = false}; return true' - srv.admin(req) + resp = srv.admin(req) + assert_admin_success(resp) def set_cluster_ro(self, read_only_list): assert len(self.servers) == len(read_only_list) diff --git a/test/suites/test_push.py b/test/suites/test_push.py index bd14455b..24a1ae7d 100644 --- a/test/suites/test_push.py +++ b/test/suites/test_push.py @@ -7,68 +7,58 @@ import unittest import tarantool from .lib.tarantool_server import TarantoolServer +from .utils import assert_admin_success def create_server(): srv = TarantoolServer() srv.script = 'test/suites/box.lua' srv.start() - srv.admin("box.schema.user.create('test', {password = 'test', " - "if_not_exists = true})") - srv.admin("box.schema.user.grant('test', 'read,write,execute', 'universe')") - - # Create server_function (for testing purposes). - srv.admin(""" - function server_function() - x = {0,0} - while x[1] < 3 do - x[1] = x[1] + 1 - box.session.push(x) + resp = srv.admin(""" + box.schema.user.create('test', {password = 'test', if_not_exists = true}) + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) + + function server_function() + x = {0,0} + while x[1] < 3 do + x[1] = x[1] + 1 + box.session.push(x) + end + return x end - return x - end - """) - # Create tester space and on_replace trigger (for testing purposes). - srv.admin(""" - box.schema.create_space( - 'tester', { - format = { - {name = 'id', type = 'unsigned'}, - {name = 'name', type = 'string'}, - } - }) - """) - srv.admin(""" - box.space.tester:create_index( - 'primary_index', { - parts = { - {field = 1, type = 'unsigned'}, - } - }) - """) - srv.admin(""" - box.space.tester:create_index( - 'primary_index', { - parts = { - {field = 1, type = 'unsigned'}, - } - }) - """) - srv.admin(""" - function on_replace_callback() - x = {0,0} - while x[1] < 300 do - x[1] = x[1] + 100 - box.session.push(x) + box.schema.create_space( + 'tester', { + format = { + {name = 'id', type = 'unsigned'}, + {name = 'name', type = 'string'}, + } + }) + + box.space.tester:create_index( + 'primary_index', { + parts = { + {field = 1, type = 'unsigned'}, + }, + if_not_exists = true, + }) + + function on_replace_callback() + x = {0,0} + while x[1] < 300 do + x[1] = x[1] + 100 + box.session.push(x) + end end - end - """) - srv.admin(""" - box.space.tester:on_replace( - on_replace_callback - ) + + box.space.tester:on_replace( + on_replace_callback + ) + + return true """) + assert_admin_success(resp) return srv diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 1402616d..6175c958 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -1,7 +1,7 @@ """ This module tests space and index schema fetch. """ -# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods,too-many-branches,too-many-statements +# pylint: disable=missing-class-docstring,missing-function-docstring,fixme,too-many-public-methods,too-many-branches,too-many-statements,duplicate-code import sys import unittest @@ -12,6 +12,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_constraints_test +from .utils import assert_admin_success # FIXME: I'm quite sure that there is a simpler way to count @@ -54,29 +55,35 @@ def setUpClass(cls): cls.srv = TarantoolServer() cls.srv.script = 'test/suites/box.lua' cls.srv.start() - cls.srv.admin("box.schema.user.create('test', {password = 'test', if_not_exists = true})") - cls.srv.admin("box.schema.user.grant('test', 'read,write,execute', 'universe')") - - # Create server_function and tester space (for fetch_schema opt testing purposes). - cls.srv.admin("function server_function() return 2+2 end") - cls.srv.admin(""" - box.schema.create_space( - 'tester', { - format = { - {name = 'id', type = 'unsigned'}, - {name = 'name', type = 'string', is_nullable = true}, - } - }) - """) - cls.srv.admin(""" - box.space.tester:create_index( - 'primary_index', { - parts = { - {field = 1, type = 'unsigned'}, - } - }) + resp = cls.srv.admin(""" + box.schema.user.create('test', {password = 'test', if_not_exists = true}) + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) + + function server_function() return 2+2 end + + box.schema.create_space( + 'tester', { + format = { + {name = 'id', type = 'unsigned'}, + {name = 'name', type = 'string', is_nullable = true}, + }, + if_not_exists = true, + }) + + box.space.tester:create_index( + 'primary_index', { + parts = { + {field = 1, type = 'unsigned'}, + }, + if_not_exists = true, + }) + + box.space.tester:insert({1, null}) + + return true """) - cls.srv.admin("box.space.tester:insert({1, null})") + assert_admin_success(resp) cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], encoding=cls.encoding, user='test', password='test') @@ -112,31 +119,34 @@ def setUpClass(cls): """) if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): - cls.srv.admin(""" - box.schema.create_space( - 'constr_tester_1', { - format = { - { name = 'id', type = 'unsigned' }, - { name = 'payload', type = 'number' }, - } - }) - box.space.constr_tester_1:create_index('I1', { parts = {'id'} }) - - box.space.constr_tester_1:replace({1, 999}) - - box.schema.create_space( - 'constr_tester_2', { - format = { - { name = 'id', type = 'unsigned' }, - { name = 'table1_id', type = 'unsigned', - foreign_key = { fk_video = { space = 'constr_tester_1', field = 'id' } }, - }, - { name = 'payload', type = 'number' }, - } - }) - box.space.constr_tester_2:create_index('I1', { parts = {'id'} }) - box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} }) + resp = cls.srv.admin(""" + box.schema.create_space( + 'constr_tester_1', { + format = { + { name = 'id', type = 'unsigned' }, + { name = 'payload', type = 'number' }, + } + }) + box.space.constr_tester_1:create_index('I1', { parts = {'id'} }) + + box.space.constr_tester_1:replace({1, 999}) + + box.schema.create_space( + 'constr_tester_2', { + format = { + { name = 'id', type = 'unsigned' }, + { name = 'table1_id', type = 'unsigned', + foreign_key = { fk_video = { space = 'constr_tester_1', field = 'id' } }, + }, + { name = 'payload', type = 'number' }, + } + }) + box.space.constr_tester_2:create_index('I1', { parts = {'id'} }) + box.space.constr_tester_2:create_index('I2', { parts = {'table1_id'} }) + + return true """) + assert_admin_success(resp) def setUp(self): # prevent a remote tarantool from clean our session @@ -603,10 +613,13 @@ def tearDownClass(cls): # We need to drop spaces with foreign keys with predetermined order, # otherwise remote server clean() will fail to clean up resources. if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.10.0'): - cls.srv.admin(""" - box.space.constr_tester_2:drop() - box.space.constr_tester_1:drop() + resp = cls.srv.admin(""" + box.space.constr_tester_2:drop() + box.space.constr_tester_1:drop() + + return true """) + assert_admin_success(resp) cls.con.close() cls.con_schema_disable.close() diff --git a/test/suites/test_uuid.py b/test/suites/test_uuid.py index 0a9ef06b..90943fcb 100644 --- a/test/suites/test_uuid.py +++ b/test/suites/test_uuid.py @@ -7,6 +7,7 @@ import unittest import uuid +import pkg_resources import msgpack import tarantool @@ -15,6 +16,7 @@ from .lib.tarantool_server import TarantoolServer from .lib.skip import skip_or_run_uuid_test +from .utils import assert_admin_success class TestSuiteUUID(unittest.TestCase): @@ -27,26 +29,35 @@ def setUpClass(cls): cls.srv.start() cls.adm = cls.srv.admin - cls.adm(r""" + resp = cls.adm(""" _, uuid = pcall(require, 'uuid') - box.schema.space.create('test') + box.schema.space.create('test', {if_not_exists = true}) box.space['test']:create_index('primary', { type = 'tree', parts = {1, 'string'}, - unique = true}) + unique = true, + if_not_exists = true}) - pcall(function() - box.schema.space.create('test_pk') + box.schema.user.create('test', {password = 'test', if_not_exists = true}) + box.schema.user.grant('test', 'read,write,execute', 'universe', + nil, {if_not_exists = true}) + + return true + """) + assert_admin_success(resp) + + if cls.srv.admin.tnt_version >= pkg_resources.parse_version('2.4.1'): + resp = cls.adm(""" + box.schema.space.create('test_pk', {if_not_exists = true}) box.space['test_pk']:create_index('primary', { type = 'tree', parts = {1, 'uuid'}, - unique = true}) - end) - - box.schema.user.create('test', {password = 'test', if_not_exists = true}) - box.schema.user.grant('test', 'read,write,execute', 'universe') - """) + unique = true, + if_not_exists = true}) + return true + """) + assert_admin_success(resp) cls.con = tarantool.Connection(cls.srv.host, cls.srv.args['primary'], user='test', password='test') @@ -56,7 +67,11 @@ def setUp(self): if self.srv.is_started(): self.srv.touch_lock() - self.adm("box.space['test']:truncate()") + resp = self.adm(""" + box.space['test']:truncate() + return true + """) + assert_admin_success(resp) cases = { 'uuid_1': { diff --git a/test/suites/utils.py b/test/suites/utils.py new file mode 100644 index 00000000..65e68b4c --- /dev/null +++ b/test/suites/utils.py @@ -0,0 +1,16 @@ +""" +Various test utilities. +""" + + +def assert_admin_success(resp): + """ + Util to assert admin text request response. + It is expected that request last line is `return true`. + If something went wrong on executing, Tarantool throws an error + which would be a part of return values. + """ + + assert isinstance(resp, list), f'got unexpected resp type: {type(resp)}' + assert len(resp) > 0, 'got unexpected empty resp' + assert resp[0] is True, f'got unexpected resp: {resp}' From 66e53bca472fec0efacd690ebe6c54c33a7df3f9 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 8 Aug 2023 11:14:47 +0300 Subject: [PATCH 164/190] test: fix confusing tuple value `null` is a common variable in Lua. Since the variable is not declared and strict mode is off, it is essentially `nil`. --- test/suites/test_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index 6175c958..c90ddbff 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -79,7 +79,7 @@ def setUpClass(cls): if_not_exists = true, }) - box.space.tester:insert({1, null}) + box.space.tester:insert({1, nil}) return true """) From a4b734aa46facc89fd967de1e74f9768341e56fc Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 19 Sep 2023 17:36:56 +0300 Subject: [PATCH 165/190] internal: more careful crud response process In most cases, crud API response process assumes that there are always two response values, which is rather uncommon. The reason of that is internal crud wrappers [1]. This patch reworks response process a bit to more carefully process this unusual case and to not confuse developers. 1. https://github.com/tarantool/crud/blob/53457477974fed42351cbd87f566d11e9f7e39bb/crud/common/schema.lua#L88 --- tarantool/connection.py | 51 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index ea089c08..2fab182b 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -2158,7 +2158,7 @@ def crud_insert(self, space_name: str, values: Union[tuple, list], crud_resp = call_crud(self, "crud.insert", space_name, values, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2192,7 +2192,7 @@ def crud_insert_object(self, space_name: str, values: dict, crud_resp = call_crud(self, "crud.insert_object", space_name, values, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2230,7 +2230,7 @@ def crud_insert_many(self, space_name: str, values: Union[tuple, list], if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2271,7 +2271,7 @@ def crud_insert_object_many(self, space_name: str, values: Union[tuple, list], if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2306,7 +2306,7 @@ def crud_get(self, space_name: str, key: int, opts: Optional[dict] = None) -> Cr crud_resp = call_crud(self, "crud.get", space_name, key, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2345,7 +2345,7 @@ def crud_update(self, space_name: str, key: int, operations: Optional[list] = No crud_resp = call_crud(self, "crud.update", space_name, key, operations, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2377,7 +2377,7 @@ def crud_delete(self, space_name: str, key: int, opts: Optional[dict] = None) -> crud_resp = call_crud(self, "crud.delete", space_name, key, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2411,7 +2411,7 @@ def crud_replace(self, space_name: str, values: Union[tuple, list], crud_resp = call_crud(self, "crud.replace", space_name, values, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2445,7 +2445,7 @@ def crud_replace_object(self, space_name: str, values: dict, crud_resp = call_crud(self, "crud.replace_object", space_name, values, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2483,7 +2483,7 @@ def crud_replace_many(self, space_name: str, values: Union[tuple, list], if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2524,7 +2524,7 @@ def crud_replace_object_many(self, space_name: str, values: Union[tuple, list], if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2567,7 +2567,7 @@ def crud_upsert(self, space_name: str, values: Union[tuple, list], crud_resp = call_crud(self, "crud.upsert", space_name, values, operations, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2608,7 +2608,7 @@ def crud_upsert_object(self, space_name: str, values: dict, crud_resp = call_crud(self, "crud.upsert_object", space_name, values, operations, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2646,7 +2646,7 @@ def crud_upsert_many(self, space_name: str, values_operation: Union[tuple, list] if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2687,7 +2687,7 @@ def crud_upsert_object_many(self, space_name: str, values_operation: Union[tuple if crud_resp[0] is not None: res = CrudResult(crud_resp[0]) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: errs = [] for err in crud_resp[1]: errs.append(CrudError(err)) @@ -2726,7 +2726,7 @@ def crud_select(self, space_name: str, conditions: Optional[list] = None, crud_resp = call_crud(self, "crud.select", space_name, conditions, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2758,7 +2758,7 @@ def crud_min(self, space_name: str, index_name: str, opts: Optional[dict] = None crud_resp = call_crud(self, "crud.min", space_name, index_name, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2790,7 +2790,7 @@ def crud_max(self, space_name: str, index_name: str, opts: Optional[dict] = None crud_resp = call_crud(self, "crud.max", space_name, index_name, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return CrudResult(crud_resp[0]) @@ -2819,9 +2819,7 @@ def crud_truncate(self, space_name: str, opts: Optional[dict] = None) -> bool: crud_resp = call_crud(self, "crud.truncate", space_name, opts) - # In absence of an error, crud does not give - # variable err as nil (as in most cases). - if len(crud_resp) != 1: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return crud_resp[0] @@ -2850,9 +2848,7 @@ def crud_len(self, space_name: str, opts: Optional[dict] = None) -> int: crud_resp = call_crud(self, "crud.len", space_name, opts) - # In absence of an error, crud does not give - # variable err as nil (as in most cases). - if len(crud_resp) != 1: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return crud_resp[0] @@ -2877,9 +2873,7 @@ def crud_storage_info(self, opts: Optional[dict] = None) -> dict: crud_resp = call_crud(self, "crud.storage_info", opts) - # In absence of an error, crud does not give - # variable err as nil (as in most cases). - if len(crud_resp) != 1: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return crud_resp[0] @@ -2915,7 +2909,7 @@ def crud_count(self, space_name: str, conditions: Optional[list] = None, crud_resp = call_crud(self, "crud.count", space_name, conditions, opts) - if crud_resp[1] is not None: + if len(crud_resp) > 1 and crud_resp[1] is not None: raise CrudModuleError(None, CrudError(crud_resp[1])) return crud_resp[0] @@ -2938,6 +2932,7 @@ def crud_stats(self, space_name: str = None) -> CrudResult: crud_resp = call_crud(self, "crud.stats", space_name) + # There are no errors in `crud.stats`. res = None if len(crud_resp.data[0]) > 0: res = CrudResult(crud_resp.data[0]) From fea0d5f751c0800b915624ad5db3d12ffe898bf6 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 19 Sep 2023 17:41:31 +0300 Subject: [PATCH 166/190] api: exception rethrow in crud Before this patch, all DatabaseError exception, except for ER_NO_SUCH_PROC and ER_ACCESS_DENIED ones, were silently ignored. It resulted in "UnboundLocalError: local variable 'crud_resp' referenced before assignment" instead of a proper error. --- CHANGELOG.md | 5 +++++ tarantool/crud.py | 1 + test/suites/crud_mock_server.lua | 21 +++++++++++++++++++++ test/suites/test_crud.py | 21 +++++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 test/suites/crud_mock_server.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e58a2b..8555c6ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Fixed +- Exception rethrow in crud API (PR #310). + ## 1.1.1 - 2023-07-19 ### Changed diff --git a/tarantool/crud.py b/tarantool/crud.py index d10726c5..3a642564 100644 --- a/tarantool/crud.py +++ b/tarantool/crud.py @@ -69,5 +69,6 @@ def call_crud(conn, *args): if exc.code in (ER_NO_SUCH_PROC, ER_ACCESS_DENIED): exc_msg = ". Ensure that you're calling crud.router and user has sufficient grants" raise DatabaseError(exc.code, exc.message + exc_msg, extra_info=exc.extra_info) from exc + raise exc return crud_resp diff --git a/test/suites/crud_mock_server.lua b/test/suites/crud_mock_server.lua new file mode 100644 index 00000000..252059df --- /dev/null +++ b/test/suites/crud_mock_server.lua @@ -0,0 +1,21 @@ +#!/usr/bin/env tarantool + +local admin_listen = os.getenv("ADMIN") +local primary_listen = os.getenv("LISTEN") + +require('console').listen(admin_listen) +box.cfg{ + listen = primary_listen, + memtx_memory = 0.1 * 1024^3, -- 0.1 GiB + pid_file = "box.pid", +} + +box.schema.user.grant('guest', 'execute', 'universe', nil, {if_not_exists = true}) + +local function mock_replace() + error('Unexpected connection error') +end + +rawset(_G, 'crud', {replace = mock_replace}) + +rawset(_G, 'ready', true) diff --git a/test/suites/test_crud.py b/test/suites/test_crud.py index d1d7e186..2bbc5513 100644 --- a/test/suites/test_crud.py +++ b/test/suites/test_crud.py @@ -22,6 +22,14 @@ def create_server(): return srv +def create_mock_server(): + srv = TarantoolServer() + srv.script = 'test/suites/crud_mock_server.lua' + srv.start() + + return srv + + @unittest.skipIf(sys.platform.startswith("win"), "Crud tests on windows platform are not supported: " "complexity of the vshard replicaset configuration") @@ -33,14 +41,19 @@ def setUpClass(cls): print('-' * 70, file=sys.stderr) # Create server and extract helpful fields for tests. cls.srv = create_server() + cls.mock_srv = create_mock_server() cls.host = cls.srv.host cls.port = cls.srv.args['primary'] + cls.mock_host = cls.mock_srv.host + cls.mock_port = cls.mock_srv.args['primary'] def setUp(self): time.sleep(1) # Open connections to instance. self.conn = tarantool.Connection(host=self.host, port=self.port, user='guest', password='', fetch_schema=False) + self.mock_conn = tarantool.Connection(host=self.mock_host, port=self.mock_port, + user='guest', password='', fetch_schema=False) self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port, user='guest', password='', fetch_schema=False) self.conn_pool = tarantool.ConnectionPool([{'host': self.host, 'port': self.port}], @@ -736,9 +749,15 @@ def test_crud_module_via_pool_connection(self): # Exception try testing. self._exception_operation_with_crud(testing_function, case, mode=tarantool.Mode.RW) + def test_error_rethrow(self): + self.assertRaisesRegex( + DatabaseError, "Unexpected connection error", + lambda: self.mock_conn.crud_replace('tester', [2, 100, 'Alice'], {'timeout': 10})) + def tearDown(self): # Close connections to instance. self.conn.close() + self.mock_conn.close() self.conn_mesh.close() self.conn_pool.close() @@ -747,3 +766,5 @@ def tearDownClass(cls): # Stop instance. cls.srv.stop() cls.srv.clean() + cls.mock_srv.stop() + cls.mock_srv.clean() From 90402e8926cdb07ba35835cc9fd6591d51fe3510 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 19 Sep 2023 18:58:46 +0300 Subject: [PATCH 167/190] datetime: support big values for some platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, tarantool.Datetime constructor used datetime.fromtimestamp function to build a new datetime [1], except for negative timestamps for Windows platform. This constructor branch is used on each Tarantool datetime encoding or while building a tarantool.Datetime object from timestamp. datetime.fromtimestamp have some drawbacks: it "may raise OverflowError, if the timestamp is out of the range of values supported by the platform C localtime() or gmtime() functions, and OSError on localtime() or gmtime() failure. It’s common for this to be restricted to years in 1970 through 2038.". It had never happened on supported Unix platforms, but seem to be an issue for Windows ones. We already workaround this issue for years smaller than 1970 on Windows. After this patch, this workaround will be used for all platforms and timestamp values, allowing to provide similar behavior for platforms both restricted to years in 1970 through 2038 with localtime() or gmtime() or not. 1. https://docs.python.org/3/library/datetime.html#datetime.datetime.fromtimestamp --- CHANGELOG.md | 4 +++ tarantool/msgpack_ext/types/datetime.py | 14 ++++----- test/suites/test_datetime.py | 40 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8555c6ac..a4b400fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Exception rethrow in crud API (PR #310). +- Work with timestamps larger than year 2038 for some platforms (like Windows) (PR #311). + It covers + - building new `tarantool.Datetime` objects from timestamp, + - parsing datetime objects received from Tarantool. ## 1.1.1 - 2023-07-19 diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 1a546cfc..993df404 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -6,7 +6,6 @@ from calendar import monthrange from copy import deepcopy from datetime import datetime, timedelta -import sys import pytz @@ -314,13 +313,12 @@ def __init__(self, *, timestamp=None, year=None, month=None, timestamp += nsec // NSEC_IN_SEC nsec = nsec % NSEC_IN_SEC - if (sys.platform.startswith("win")) and (timestamp < 0): - # Fails to create a datetime from negative timestamp on Windows. - _datetime = _EPOCH + timedelta(seconds=timestamp) - else: - # Timezone-naive datetime objects are treated by many datetime methods - # as local times, so we represent time in UTC explicitly if not provided. - _datetime = datetime.fromtimestamp(timestamp, pytz.UTC) + # datetime.fromtimestamp may raise OverflowError, if the timestamp + # is out of the range of values supported by the platform C localtime() + # function, and OSError on localtime() failure. It’s common for this + # to be restricted to years from 1970 through 2038, yet we want + # to support a wider range. + _datetime = _EPOCH + timedelta(seconds=timestamp) if nsec is not None: _datetime = _datetime.replace(microsecond=nsec // NSEC_IN_MKSEC) diff --git a/test/suites/test_datetime.py b/test/suites/test_datetime.py index 9f250804..80958fdc 100644 --- a/test/suites/test_datetime.py +++ b/test/suites/test_datetime.py @@ -153,6 +153,24 @@ def test_datetime_class_api_wth_tz(self): 'type': ValueError, 'msg': 'Failed to create datetime with ambiguous timezone "AET"' }, + 'under_min_timestamp_1': { + 'args': [], + 'kwargs': {'timestamp': -62135596801}, + 'type': OverflowError, + 'msg': 'date value out of range' + }, + 'under_min_timestamp_2': { + 'args': [], + 'kwargs': {'timestamp': -62135596800, 'nsec': -1}, + 'type': OverflowError, + 'msg': 'date value out of range' + }, + 'over_max_timestamp': { + 'args': [], + 'kwargs': {'timestamp': 253402300800}, + 'type': OverflowError, + 'msg': 'date value out of range' + }, } def test_datetime_class_invalid_init(self): @@ -293,6 +311,28 @@ def test_datetime_class_invalid_init(self): 'tarantool': r"datetime.new({timestamp=1661969274, nsec=308543321, " r"tz='Europe/Moscow'})", }, + 'min_datetime': { # Python datetime.MINYEAR is 1. + 'python': tarantool.Datetime(year=1, month=1, day=1, hour=0, minute=0, sec=0), + 'msgpack': (b'\x00\x09\x6e\x88\xf1\xff\xff\xff'), + 'tarantool': r"datetime.new({year=1, month=1, day=1, hour=0, min=0, sec=0})", + }, + 'max_datetime': { # Python datetime.MAXYEAR is 9999. + 'python': tarantool.Datetime(year=9999, month=12, day=31, hour=23, minute=59, sec=59, + nsec=999999999), + 'msgpack': (b'\x7f\x41\xf4\xff\x3a\x00\x00\x00\xff\xc9\x9a\x3b\x00\x00\x00\x00'), + 'tarantool': r"datetime.new({year=9999, month=12, day=31, hour=23, min=59, sec=59," + r"nsec=999999999})", + }, + 'min_datetime_timestamp': { # Python datetime.MINYEAR is 1. + 'python': tarantool.Datetime(timestamp=-62135596800), + 'msgpack': (b'\x00\x09\x6e\x88\xf1\xff\xff\xff'), + 'tarantool': r"datetime.new({timestamp=-62135596800})", + }, + 'max_datetime_timestamp': { # Python datetime.MAXYEAR is 9999. + 'python': tarantool.Datetime(timestamp=253402300799, nsec=999999999), + 'msgpack': (b'\x7f\x41\xf4\xff\x3a\x00\x00\x00\xff\xc9\x9a\x3b\x00\x00\x00\x00'), + 'tarantool': r"datetime.new({timestamp=253402300799, nsec=999999999})", + }, } def test_msgpack_decode(self): From e104037337090e679830a2411ab822ed34bb2a0d Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 20 Sep 2023 09:27:36 +0300 Subject: [PATCH 168/190] release 1.1.2 Overview This release introduce several bugfixes and behavior improvements. Fixed - Exception rethrow in crud API (PR #310). - Work with timestamps larger than year 2038 for some platforms (like Windows) (PR #311). It covers - building new `tarantool.Datetime` objects from timestamp, - parsing datetime objects received from Tarantool. --- CHANGELOG.md | 2 +- debian/changelog | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4b400fc..cf674622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. 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 +## 1.1.2 - 2023-09-20 ### Fixed - Exception rethrow in crud API (PR #310). diff --git a/debian/changelog b/debian/changelog index 429c65a1..c8d5153d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,19 @@ +python3-tarantool (1.1.2-0) unstable; urgency=medium + + ## Overview + + This release introduce several bugfixes and behavior improvements. + + ## Fixed + + - Exception rethrow in crud API (PR #310). + - Work with timestamps larger than year 2038 for some platforms (like Windows) (PR #311). + It covers + - building new `tarantool.Datetime` objects from timestamp, + - parsing datetime objects received from Tarantool. + + -- Georgy Moiseev Wed, 20 Sep 2023 10:00:00 +0300 + python3-tarantool (1.1.1-0) unstable; urgency=medium ## Overview From fc2323fe9de4af57c44e02d2058db6d1dddf53ae Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 3 Oct 2023 13:38:31 +0300 Subject: [PATCH 169/190] pylint: disable new too-many-arguments cases Disable newly uncovered cases of R0913 too-many-arguments. We don't plan to change existing public API now. Since pylint 3.0.0, issue [1] has been fixed, which uncovered several new too-many-arguments cases. 1. https://github.com/pylint-dev/pylint/issues/8667 --- tarantool/connection.py | 8 ++++++++ tarantool/connection_pool.py | 9 +++++++++ tarantool/msgpack_ext/types/datetime.py | 2 +- tarantool/msgpack_ext/types/interval.py | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tarantool/connection.py b/tarantool/connection.py index 2fab182b..17b43b64 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -215,6 +215,7 @@ def delete(self, space_name, key, *, index=None, on_push=None, on_push_ctx=None) """ Reference implementation: :meth:`~tarantool.Connection.delete`. """ + # pylint: disable=too-many-arguments raise NotImplementedError @@ -224,6 +225,7 @@ def upsert(self, space_name, tuple_value, op_list, *, index=None, """ Reference implementation: :meth:`~tarantool.Connection.upsert`. """ + # pylint: disable=too-many-arguments raise NotImplementedError @@ -232,6 +234,7 @@ def update(self, space_name, key, op_list, *, index=None, on_push=None, on_push_ """ Reference implementation: :meth:`~tarantool.Connection.update`. """ + # pylint: disable=too-many-arguments raise NotImplementedError @@ -249,6 +252,7 @@ def select(self, space_name, key, *, offset=None, limit=None, """ Reference implementation: :meth:`~tarantool.Connection.select`. """ + # pylint: disable=too-many-arguments raise NotImplementedError @@ -1618,6 +1622,7 @@ def delete(self, space_name, key, *, index=0, on_push=None, on_push_ctx=None): .. _delete: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/delete/ """ + # pylint: disable=too-many-arguments self._schemaful_connection_check() @@ -1680,6 +1685,7 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, on_push=None, on_ .. _upsert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/upsert/ """ + # pylint: disable=too-many-arguments self._schemaful_connection_check() @@ -1771,6 +1777,7 @@ def update(self, space_name, key, op_list, *, index=0, on_push=None, on_push_ctx .. _update: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/update/ """ + # pylint: disable=too-many-arguments self._schemaful_connection_check() @@ -1961,6 +1968,7 @@ def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, i .. _select: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/select/ """ + # pylint: disable=too-many-arguments self._schemaful_connection_check() diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 1008991f..3c1e72ac 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -820,6 +820,7 @@ def replace(self, space_name, values, *, mode=Mode.RW, on_push=None, on_push_ctx .. _replace: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/replace/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'replace', space_name, values, on_push=on_push, on_push_ctx=on_push_ctx) @@ -850,6 +851,7 @@ def insert(self, space_name, values, *, mode=Mode.RW, on_push=None, on_push_ctx= .. _insert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/insert/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'insert', space_name, values, on_push=on_push, on_push_ctx=on_push_ctx) @@ -883,6 +885,7 @@ def delete(self, space_name, key, *, index=0, mode=Mode.RW, on_push=None, on_pus .. _delete: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/delete/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'delete', space_name, key, index=index, on_push=on_push, on_push_ctx=on_push_ctx) @@ -920,6 +923,7 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, mode=Mode.RW, .. _upsert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/upsert/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'upsert', space_name, tuple_value, op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) @@ -957,6 +961,7 @@ def update(self, space_name, key, op_list, *, index=0, mode=Mode.RW, .. _update: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/update/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'update', space_name, key, op_list, index=index, on_push=on_push, on_push_ctx=on_push_ctx) @@ -1023,6 +1028,7 @@ def select(self, space_name, key, *, offset=0, limit=0xffffffff, .. _select: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/select/ """ + # pylint: disable=too-many-arguments return self._send(mode, 'select', space_name, key, offset=offset, limit=limit, index=index, iterator=iterator, on_push=on_push, on_push_ctx=on_push_ctx) @@ -1214,6 +1220,7 @@ def crud_update(self, space_name, key, operations=None, opts=None, *, mode=Mode. :raise: :exc:`~tarantool.error.CrudModuleError`, :exc:`~tarantool.error.DatabaseError` """ + # pylint: disable=too-many-arguments return self._send(mode, 'crud_update', space_name, key, operations, opts) @@ -1379,6 +1386,7 @@ def crud_upsert(self, space_name, values, operations=None, opts=None, *, mode=Mo :raise: :exc:`~tarantool.error.CrudModuleError`, :exc:`~tarantool.error.DatabaseError` """ + # pylint: disable=too-many-arguments return self._send(mode, 'crud_upsert', space_name, values, operations, opts) @@ -1409,6 +1417,7 @@ def crud_upsert_object(self, space_name, values, operations=None, opts=None, *, :raise: :exc:`~tarantool.error.CrudModuleError`, :exc:`~tarantool.error.DatabaseError` """ + # pylint: disable=too-many-arguments return self._send(mode, 'crud_upsert_object', space_name, values, operations, opts) diff --git a/tarantool/msgpack_ext/types/datetime.py b/tarantool/msgpack_ext/types/datetime.py index 993df404..d7068f57 100644 --- a/tarantool/msgpack_ext/types/datetime.py +++ b/tarantool/msgpack_ext/types/datetime.py @@ -281,7 +281,7 @@ def __init__(self, *, timestamp=None, year=None, month=None, .. _datetime.new(): https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime/new/ """ - # pylint: disable=too-many-branches,too-many-locals,too-many-statements + # pylint: disable=too-many-branches,too-many-locals,too-many-statements,too-many-arguments tzinfo = None if tz != '': diff --git a/tarantool/msgpack_ext/types/interval.py b/tarantool/msgpack_ext/types/interval.py index e44dcce0..734a9088 100644 --- a/tarantool/msgpack_ext/types/interval.py +++ b/tarantool/msgpack_ext/types/interval.py @@ -149,6 +149,7 @@ def __init__(self, *, year=0, month=0, week=0, :raise: :exc:`ValueError` """ + # pylint: disable=too-many-arguments self.year = year self.month = month From 8549c1c8e7ad480cef8fdedb10326ef1cdfb2fea Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 3 Oct 2023 14:07:58 +0300 Subject: [PATCH 170/190] pylint: disable unnecessary-lambda in test Disable unnecessary-lambda lint warning in test. The warning had been uncovered since pylint 3.0.0, but it's not clear what exactly is the reason since the only CHANGELOG entry related to unnecessary-lambda declares that false positive cases were fixed [1]. 1. https://github.com/pylint-dev/pylint/issues/8496 --- test/suites/test_interval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/test_interval.py b/test/suites/test_interval.py index c241b312..2b6f6abe 100644 --- a/test/suites/test_interval.py +++ b/test/suites/test_interval.py @@ -616,7 +616,7 @@ def test_unknown_adjust_decode(self): } def test_out_of_range(self): - # pylint: disable=cell-var-from-loop + # pylint: disable=cell-var-from-loop,unnecessary-lambda for name, case in self.out_of_range_cases.items(): with self.subTest(msg=name): From e5fe54efafac49e6dfd0783ff5f522b4e4b714d3 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 3 Oct 2023 14:18:59 +0300 Subject: [PATCH 171/190] doc: fix spelling --- tarantool/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarantool/utils.py b/tarantool/utils.py index c7d9e246..b7ab436d 100644 --- a/tarantool/utils.py +++ b/tarantool/utils.py @@ -1,5 +1,5 @@ """ -This module provides untility functions for the package. +This module provides utility functions for the package. """ from base64 import decodebytes as base64_decode From 3c8eb9da94c5e1665318d994219efa104e5fb78e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Tue, 3 Oct 2023 14:10:32 +0300 Subject: [PATCH 172/190] deps: fixate linters version Version lock is based on a latest version available for specific Python. --- requirements-test.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/requirements-test.txt b/requirements-test.txt index 94d16351..56f4b595 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,6 +1,8 @@ git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-compliance pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' -pylint -flake8 -codespell +pylint ~= 3.0 ; python_version >= '3.8' +pylint ~= 2.13 ; python_version < '3.8' +flake8 ~= 6.1 ; python_version >= '3.8' +flake8 ~= 5.0 ; python_version < '3.8' +codespell ~= 2.2 From 52165d34f9e63331e16e13c6e19683791d82d756 Mon Sep 17 00:00:00 2001 From: Albert Skalt Date: Wed, 1 Nov 2023 12:27:03 +0300 Subject: [PATCH 173/190] doc: fix typo in docstring --- tarantool/mesh_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index ebbc1a40..74b8af43 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -298,7 +298,7 @@ def __init__(self, host=None, port=None, :paramref:`~tarantool.MeshConnection.params.addrs` list. :param port: Refer to - :paramref:`~tarantool.Connection.params.host`. + :paramref:`~tarantool.Connection.params.port`. Value would be used to add one more server in :paramref:`~tarantool.MeshConnection.params.addrs` list. From b0833e705cbae30b94b0ef0b82e2657ec89d5b93 Mon Sep 17 00:00:00 2001 From: Albert Skalt Date: Thu, 2 Nov 2023 11:50:14 +0300 Subject: [PATCH 174/190] ci: run tests with tarantool CE master --- .github/workflows/testing.yml | 37 +++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 88fa08d7..82f299b4 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -27,6 +27,7 @@ jobs: - '2.8' - '2.10' - '2.11' + - 'master' python: - '3.6' - '3.7' @@ -57,11 +58,45 @@ jobs: - name: Clone the connector uses: actions/checkout@v3 + - name: Setup tt + run: | + curl -L https://tarantool.io/release/2/installer.sh | sudo bash + sudo apt install -y tt + tt version + tt init + - name: Install tarantool ${{ matrix.tarantool }} + if: matrix.tarantool != 'master' uses: tarantool/setup-tarantool@v2 with: tarantool-version: ${{ matrix.tarantool }} + - name: Get Tarantool master latest commit + if: matrix.tarantool == 'master' + run: | + commit_hash=$(git ls-remote https://github.com/tarantool/tarantool.git --branch master | head -c 8) + echo "LATEST_COMMIT=${commit_hash}" >> $GITHUB_ENV + shell: bash + + - name: Cache Tarantool master + if: matrix.tarantool == 'master' + id: cache-latest + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/bin + ${{ github.workspace }}/include + key: cache-latest-${{ env.LATEST_COMMIT }} + + - name: Setup Tarantool master + if: matrix.tarantool == 'master' && steps.cache-latest.outputs.cache-hit != 'true' + run: | + tt install tarantool master + + - name: Add Tarantool master to PATH + if: matrix.tarantool == 'master' + run: echo "${GITHUB_WORKSPACE}/bin" >> $GITHUB_PATH + - name: Setup Python for tests uses: actions/setup-python@v4 with: @@ -86,8 +121,6 @@ jobs: - name: Install the crud module for testing purposes run: | - curl -L https://tarantool.io/release/2/installer.sh | bash - sudo apt install -y tt tt rocks install crud - name: Run tests From 2983de854aa958b3890fccdd2eae8033480120de Mon Sep 17 00:00:00 2001 From: Albert Skalt Date: Tue, 31 Oct 2023 23:15:54 +0300 Subject: [PATCH 175/190] conn: create from socket fd This patch adds the ability to create Tarantool connection using an existing socket fd. To achieve this, several changes have been made to work with non-blocking sockets, as `socket.socketpair` creates such [1]. The authentication [2] might have already occured when we establish such a connection. If that's the case, there is no need to pass 'user' argument. On success, connect takes ownership of the `socket_fd`. 1. https://github.com/tarantool/tarantool/pull/8944 2. https://www.tarantool.io/en/doc/latest/dev_guide/internals/iproto/authentication/ Closes #304 --- CHANGELOG.md | 5 + tarantool/__init__.py | 5 +- tarantool/connection.py | 104 ++++++++++++++++-- tarantool/connection_pool.py | 29 +++-- tarantool/const.py | 6 + tarantool/mesh_connection.py | 39 ++++++- test/suites/__init__.py | 3 +- test/suites/lib/skip.py | 11 ++ test/suites/sidecar.py | 16 +++ test/suites/test_mesh.py | 1 - test/suites/test_socket_fd.py | 200 ++++++++++++++++++++++++++++++++++ 11 files changed, 394 insertions(+), 25 deletions(-) create mode 100644 test/suites/sidecar.py create mode 100644 test/suites/test_socket_fd.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cf674622..f3bd49f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Added +- The ability to connect to the Tarantool using an existing socket fd (#304). + ## 1.1.2 - 2023-09-20 ### Fixed diff --git a/tarantool/__init__.py b/tarantool/__init__.py index 91f80e10..d7e99358 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -51,7 +51,7 @@ __version__ = '0.0.0-dev' -def connect(host="localhost", port=33013, user=None, password=None, +def connect(host="localhost", port=33013, socket_fd=None, user=None, password=None, encoding=ENCODING_DEFAULT, transport=DEFAULT_TRANSPORT, ssl_key_file=DEFAULT_SSL_KEY_FILE, ssl_cert_file=DEFAULT_SSL_CERT_FILE, @@ -64,6 +64,8 @@ def connect(host="localhost", port=33013, user=None, password=None, :param port: Refer to :paramref:`~tarantool.Connection.params.port`. + :param socket_fd: Refer to :paramref:`~tarantool.Connection.params.socket_fd`. + :param user: Refer to :paramref:`~tarantool.Connection.params.user`. :param password: Refer to @@ -93,6 +95,7 @@ def connect(host="localhost", port=33013, user=None, password=None, """ return Connection(host, port, + socket_fd=socket_fd, user=user, password=password, socket_timeout=SOCKET_TIMEOUT, diff --git a/tarantool/connection.py b/tarantool/connection.py index 17b43b64..1284fa25 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -4,6 +4,7 @@ # pylint: disable=too-many-lines,duplicate-code import os +import select import time import errno from enum import Enum @@ -51,6 +52,9 @@ RECONNECT_DELAY, DEFAULT_TRANSPORT, SSL_TRANSPORT, + DEFAULT_HOST, + DEFAULT_PORT, + DEFAULT_SOCKET_FD, DEFAULT_SSL_KEY_FILE, DEFAULT_SSL_CERT_FILE, DEFAULT_SSL_CA_FILE, @@ -594,7 +598,10 @@ class Connection(ConnectionInterface): :value: :exc:`~tarantool.error.CrudModuleError` """ - def __init__(self, host, port, + def __init__(self, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + socket_fd=DEFAULT_SOCKET_FD, user=None, password=None, socket_timeout=SOCKET_TIMEOUT, @@ -623,8 +630,11 @@ def __init__(self, host, port, Unix sockets. :type host: :obj:`str` or :obj:`None` - :param port: Server port or Unix socket path. - :type port: :obj:`int` or :obj:`str` + :param port: Server port, or Unix socket path. + :type port: :obj:`int` or :obj:`str` or :obj:`None` + + :param socket_fd: socket fd number. + :type socket_fd: :obj:`int` or :obj:`None` :param user: User name for authentication on the Tarantool server. @@ -804,6 +814,18 @@ def __init__(self, host, port, """ # pylint: disable=too-many-arguments,too-many-locals,too-many-statements + if host is None and port is None and socket_fd is None: + raise ConfigurationError("need to specify host/port, " + "port (in case of Unix sockets) " + "or socket_fd") + + if socket_fd is not None and (host is not None or port is not None): + raise ConfigurationError("specifying both socket_fd and host/port is not allowed") + + if host is not None and port is None: + raise ConfigurationError("when specifying host, " + "it is also necessary to specify port") + if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'): raise ConfigurationError("msgpack>=1.0.0 only supports None and " + "'utf-8' encoding option values") @@ -820,6 +842,7 @@ def __init__(self, host, port, recv.restype = ctypes.c_int self.host = host self.port = port + self.socket_fd = socket_fd self.user = user self.password = password self.socket_timeout = socket_timeout @@ -897,10 +920,37 @@ def connect_basic(self): :meta private: """ - if self.host is None: - self.connect_unix() - else: + if self.socket_fd is not None: + self.connect_socket_fd() + elif self.host is not None: self.connect_tcp() + else: + self.connect_unix() + + def connect_socket_fd(self): + """ + Establish a connection using an existing socket fd. + + +---------------------+--------------------------+-------------------------+ + | socket_fd / timeout | >= 0 | `None` | + +=====================+==========================+=========================+ + | blocking | Set non-blocking socket | Don't change, `select` | + | | lib call `select` | isn't needed | + +---------------------+--------------------------+-------------------------+ + | non-blocking | Don't change, socket lib | Don't change, call | + | | call `select` | `select` ourselves | + +---------------------+--------------------------+-------------------------+ + + :meta private: + """ + + self.connected = True + if self._socket: + self._socket.close() + + self._socket = socket.socket(fileno=self.socket_fd) + if self.socket_timeout is not None: + self._socket.settimeout(self.socket_timeout) def connect_tcp(self): """ @@ -1124,6 +1174,11 @@ def _recv(self, to_read): while to_read > 0: try: tmp = self._socket.recv(to_read) + except BlockingIOError: + ready, _, _ = select.select([self._socket.fileno()], [], [], self.socket_timeout) + if not ready: + raise NetworkError(TimeoutError()) # pylint: disable=raise-missing-from + continue except OverflowError as exc: self._socket.close() err = socket.error( @@ -1163,6 +1218,41 @@ def _read_response(self): # Read the packet return self._recv(length) + def _sendall(self, bytes_to_send): + """ + Sends bytes to the transport (socket). + + :param bytes_to_send: Message to send. + :type bytes_to_send: :obj:`bytes` + + :raise: :exc:`~tarantool.error.NetworkError` + + :meta private: + """ + + total_sent = 0 + while total_sent < len(bytes_to_send): + try: + sent = self._socket.send(bytes_to_send[total_sent:]) + if sent == 0: + err = socket.error( + errno.ECONNRESET, + "Lost connection to server during query" + ) + raise NetworkError(err) + total_sent += sent + except BlockingIOError as exc: + total_sent += exc.characters_written + _, ready, _ = select.select([], [self._socket.fileno()], [], self.socket_timeout) + if not ready: + raise NetworkError(TimeoutError()) # pylint: disable=raise-missing-from + except socket.error as exc: + err = socket.error( + errno.ECONNRESET, + "Lost connection to server during query" + ) + raise NetworkError(err) from exc + def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): """ Send request without trying to reconnect. @@ -1191,7 +1281,7 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None): response = None while True: try: - self._socket.sendall(bytes(request)) + self._sendall(bytes(request)) response = request.response_class(self, self._read_response()) break except SchemaReloadException as exc: diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 3c1e72ac..7bbd4e16 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -115,7 +115,7 @@ class PoolUnit(): addr: dict """ - ``{"host": host, "port": port}`` info. + ``{"host": host, "port": port, "socket_fd": socket_fd}`` info. :type: :obj:`dict` """ @@ -161,6 +161,14 @@ class PoolUnit(): :type: :obj:`bool` """ + def get_address(self): + """ + Get an address string representation. + """ + if self.addr['socket_fd'] is not None: + return f'fd://{self.addr["socket_fd"]}' + return f'{self.addr["host"]}:{self.addr["port"]}' + # Based on https://realpython.com/python-interface/ class StrategyInterface(metaclass=abc.ABCMeta): @@ -398,6 +406,7 @@ def __init__(self, { "host': "str" or None, # mandatory "port": int or "str", # mandatory + "socket_fd": int, # optional "transport": "str", # optional "ssl_key_file": "str", # optional "ssl_cert_file": "str", # optional @@ -499,6 +508,7 @@ def __init__(self, conn=Connection( host=addr['host'], port=addr['port'], + socket_fd=addr['socket_fd'], user=user, password=password, socket_timeout=socket_timeout, @@ -529,15 +539,16 @@ def _make_key(self, addr): """ Make a unique key for a server based on its address. - :param addr: `{"host": host, "port": port}` dictionary. + :param addr: `{"host": host, "port": port, "socket_fd": socket_fd}` dictionary. :type addr: :obj:`dict` :rtype: :obj:`str` :meta private: """ - - return f"{addr['host']}:{addr['port']}" + if addr['socket_fd'] is None: + return f"{addr['host']}:{addr['port']}" + return addr['socket_fd'] def _get_new_state(self, unit): """ @@ -557,7 +568,7 @@ def _get_new_state(self, unit): try: conn.connect() except NetworkError as exc: - msg = (f"Failed to connect to {unit.addr['host']}:{unit.addr['port']}, " + msg = (f"Failed to connect to {unit.get_address()}, " f"reason: {repr(exc)}") warn(msg, ClusterConnectWarning) return InstanceState(Status.UNHEALTHY) @@ -565,7 +576,7 @@ def _get_new_state(self, unit): try: resp = conn.call('box.info') except NetworkError as exc: - msg = (f"Failed to get box.info for {unit.addr['host']}:{unit.addr['port']}, " + msg = (f"Failed to get box.info for {unit.get_address()}, " f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) @@ -573,7 +584,7 @@ def _get_new_state(self, unit): try: read_only = resp.data[0]['ro'] except (IndexError, KeyError) as exc: - msg = (f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + msg = (f"Incorrect box.info response from {unit.get_address()}" f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) @@ -582,11 +593,11 @@ def _get_new_state(self, unit): status = resp.data[0]['status'] if status != 'running': - msg = f"{unit.addr['host']}:{unit.addr['port']} instance status is not 'running'" + msg = f"{unit.get_address()} instance status is not 'running'" warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) except (IndexError, KeyError) as exc: - msg = (f"Incorrect box.info response from {unit.addr['host']}:{unit.addr['port']}" + msg = (f"Incorrect box.info response from {unit.get_address()}" f"reason: {repr(exc)}") warn(msg, PoolTolopogyWarning) return InstanceState(Status.UNHEALTHY) diff --git a/tarantool/const.py b/tarantool/const.py index 1e2b0895..53749cec 100644 --- a/tarantool/const.py +++ b/tarantool/const.py @@ -103,6 +103,12 @@ IPROTO_FEATURE_SPACE_AND_INDEX_NAMES = 5 IPROTO_FEATURE_WATCH_ONCE = 6 +# Default value for host. +DEFAULT_HOST = None +# Default value for port. +DEFAULT_PORT = None +# Default value for socket_fd. +DEFAULT_SOCKET_FD = None # Default value for connection timeout (seconds) CONNECTION_TIMEOUT = None # Default value for socket timeout (seconds) diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 74b8af43..0f86c0dd 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -28,6 +28,9 @@ DEFAULT_SSL_PASSWORD, DEFAULT_SSL_PASSWORD_FILE, CLUSTER_DISCOVERY_DELAY, + DEFAULT_HOST, + DEFAULT_SOCKET_FD, + DEFAULT_PORT, ) from tarantool.request import ( @@ -35,6 +38,9 @@ ) default_addr_opts = { + 'host': DEFAULT_HOST, + 'port': DEFAULT_PORT, + 'socket_fd': DEFAULT_SOCKET_FD, 'transport': DEFAULT_TRANSPORT, 'ssl_key_file': DEFAULT_SSL_KEY_FILE, 'ssl_cert_file': DEFAULT_SSL_CERT_FILE, @@ -91,7 +97,8 @@ def parse_error(uri, msg): return parse_error(uri, 'port should be a number') for key, val in default_addr_opts.items(): - result[key] = val + if key not in result: + result[key] = val if opts_str != "": for opt_str in opts_str.split('&'): @@ -127,9 +134,6 @@ def format_error(address, err): if not isinstance(address, dict): return format_error(address, 'address must be a dict') - if 'port' not in address or address['port'] is None: - return format_error(address, 'port is not set or None') - result = {} for key, val in address.items(): result[key] = val @@ -138,6 +142,17 @@ def format_error(address, err): if key not in result: result[key] = val + if result['socket_fd'] is not None: + # Looks like socket fd. + if result['host'] is not None or result['port'] is not None: + return format_error(result, + "specifying both socket_fd and host/port is not allowed") + + if not isinstance(result['socket_fd'], int): + return format_error(result, + 'socket_fd must be an int') + return result, None + if isinstance(result['port'], int): # Looks like an inet address. @@ -192,6 +207,7 @@ def update_connection(conn, address): conn.host = address["host"] conn.port = address["port"] + conn.socket_fd = address["socket_fd"] conn.transport = address['transport'] conn.ssl_key_file = address['ssl_key_file'] conn.ssl_cert_file = address['ssl_cert_file'] @@ -268,7 +284,10 @@ class MeshConnection(Connection): Represents a connection to a cluster of Tarantool servers. """ - def __init__(self, host=None, port=None, + def __init__(self, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + socket_fd=DEFAULT_SOCKET_FD, user=None, password=None, socket_timeout=SOCKET_TIMEOUT, @@ -302,6 +321,11 @@ def __init__(self, host=None, port=None, Value would be used to add one more server in :paramref:`~tarantool.MeshConnection.params.addrs` list. + :param socket_fd: Refer to + :paramref:`~tarantol.Connection.params.socket_fd`. + Value would be used to add one more server in + :paramref:`~tarantool.MeshConnection.params.addrs` list. + :param user: Refer to :paramref:`~tarantool.Connection.params.user`. Value would be used to add one more server in @@ -447,9 +471,10 @@ def __init__(self, host=None, port=None, # Don't change user provided arguments. addrs = addrs[:] - if host and port: + if (host and port) or socket_fd: addrs.insert(0, {'host': host, 'port': port, + 'socket_fd': socket_fd, 'transport': transport, 'ssl_key_file': ssl_key_file, 'ssl_cert_file': ssl_cert_file, @@ -484,6 +509,7 @@ def __init__(self, host=None, port=None, super().__init__( host=addr['host'], port=addr['port'], + socket_fd=addr['socket_fd'], user=user, password=password, socket_timeout=socket_timeout, @@ -604,6 +630,7 @@ def _opt_refresh_instances(self): # an instance list and connect to one of new instances. current_addr = {'host': self.host, 'port': self.port, + 'socket_fd': self.socket_fd, 'transport': self.transport, 'ssl_key_file': self.ssl_key_file, 'ssl_cert_file': self.ssl_cert_file, diff --git a/test/suites/__init__.py b/test/suites/__init__.py index d56b2889..7d092585 100644 --- a/test/suites/__init__.py +++ b/test/suites/__init__.py @@ -15,6 +15,7 @@ from .test_execute import TestSuiteExecute from .test_dbapi import TestSuiteDBAPI from .test_encoding import TestSuiteEncoding +from .test_socket_fd import TestSuiteSocketFD from .test_ssl import TestSuiteSsl from .test_decimal import TestSuiteDecimal from .test_uuid import TestSuiteUUID @@ -33,7 +34,7 @@ TestSuiteEncoding, TestSuitePool, TestSuiteSsl, TestSuiteDecimal, TestSuiteUUID, TestSuiteDatetime, TestSuiteInterval, TestSuitePackage, TestSuiteErrorExt, - TestSuitePush, TestSuiteConnection, TestSuiteCrud,) + TestSuitePush, TestSuiteConnection, TestSuiteCrud, TestSuiteSocketFD) def load_tests(loader, tests, pattern): diff --git a/test/suites/lib/skip.py b/test/suites/lib/skip.py index 00b1a21d..625caf6a 100644 --- a/test/suites/lib/skip.py +++ b/test/suites/lib/skip.py @@ -306,3 +306,14 @@ def skip_or_run_iproto_basic_features_test(func): return skip_or_run_test_tarantool(func, '2.10.0', 'does not support iproto ID and iproto basic features') + + +def skip_or_run_box_session_new_tests(func): + """ + Decorator to skip or run tests that use box.session.new. + + Tarantool supports box.session.new only in current master since + commit 324872a. + See https://github.com/tarantool/tarantool/issues/8801. + """ + return skip_or_run_test_tarantool(func, '3.0.0', 'does not support box.session.new') diff --git a/test/suites/sidecar.py b/test/suites/sidecar.py new file mode 100644 index 00000000..6bee5af7 --- /dev/null +++ b/test/suites/sidecar.py @@ -0,0 +1,16 @@ +# pylint: disable=missing-module-docstring +import os + +import tarantool + +socket_fd = int(os.environ["SOCKET_FD"]) + +conn = tarantool.connect(None, None, socket_fd=socket_fd) + +# Check user. +assert conn.eval("return box.session.user()").data[0] == "test" + +# Check db operations. +conn.insert("test", [1]) +conn.insert("test", [2]) +assert conn.select("test").data == [[1], [2]] diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index 606cc7d1..a906597b 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -135,7 +135,6 @@ def test_01_contructor(self): # Verify that a bad address given at initialization leads # to an error. bad_addrs = [ - {"port": 1234}, # no host {"host": "localhost"}, # no port {"host": "localhost", "port": "1234"}, # port is str ] diff --git a/test/suites/test_socket_fd.py b/test/suites/test_socket_fd.py new file mode 100644 index 00000000..5cf94777 --- /dev/null +++ b/test/suites/test_socket_fd.py @@ -0,0 +1,200 @@ +""" +This module tests work with connection over socket fd. +""" +import os.path +# pylint: disable=missing-class-docstring,missing-function-docstring + +import socket +import sys +import unittest + +import tarantool +from .lib.skip import skip_or_run_box_session_new_tests +from .lib.tarantool_server import TarantoolServer, find_port +from .utils import assert_admin_success + + +def find_python(): + for _dir in os.environ["PATH"].split(os.pathsep): + exe = os.path.join(_dir, "python") + if os.access(exe, os.X_OK): + return os.path.abspath(exe) + raise RuntimeError("Can't find python executable in " + os.environ["PATH"]) + + +class TestSuiteSocketFD(unittest.TestCase): + EVAL_USER = "return box.session.user()" + + @classmethod + def setUpClass(cls): + print(' SOCKET FD '.center(70, '='), file=sys.stderr) + print('-' * 70, file=sys.stderr) + + cls.srv = TarantoolServer() + cls.srv.script = 'test/suites/box.lua' + cls.srv.start() + cls.tcp_port = find_port() + + # Start tcp server to test work with blocking sockets. + # pylint: disable=consider-using-f-string + resp = cls.srv.admin(""" + local socket = require('socket') + + box.cfg{} + box.schema.user.create('test', {password = 'test', if_not_exists = true}) + box.schema.user.grant('test', 'read,write,execute,create', 'universe', + nil, {if_not_exists = true}) + box.schema.user.grant('guest', 'execute', 'universe', + nil, {if_not_exists = true}) + + socket.tcp_server('0.0.0.0', %d, function(s) + if not s:nonblock(true) then + s:close() + return + end + box.session.new({ + type = 'binary', + fd = s:fd(), + user = 'test', + }) + s:detach() + end) + + box.schema.create_space('test', { + format = {{type='unsigned', name='id'}}, + if_not_exists = true, + }) + box.space.test:create_index('primary') + + return true + """ % cls.tcp_port) + assert_admin_success(resp) + + @skip_or_run_box_session_new_tests + def setUp(self): + # Prevent a remote tarantool from clean our session. + if self.srv.is_started(): + self.srv.touch_lock() + + def _get_tt_sock(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((self.srv.host, self.tcp_port)) + return sock + + def test_01_incorrect_params(self): + cases = { + "host and socket_fd": { + "args": {"host": "123", "socket_fd": 3}, + "msg": "specifying both socket_fd and host/port is not allowed", + }, + "port and socket_fd": { + "args": {"port": 14, "socket_fd": 3}, + "msg": "specifying both socket_fd and host/port is not allowed", + }, + "empty": { + "args": {}, + "msg": r"host/port.* port.* or socket_fd", + }, + "only host": { + "args": {"host": "localhost"}, + "msg": "when specifying host, it is also necessary to specify port", + }, + } + + for name, case in cases.items(): + with self.subTest(msg=name): + with self.assertRaisesRegex(tarantool.Error, case["msg"]): + tarantool.Connection(**case["args"]) + + def test_02_socket_fd_connect(self): + sock = self._get_tt_sock() + conn = tarantool.connect(None, None, socket_fd=sock.fileno()) + sock.detach() + try: + self.assertSequenceEqual(conn.eval(self.EVAL_USER), ["test"]) + finally: + conn.close() + + def test_03_socket_fd_re_auth(self): + sock = self._get_tt_sock() + conn = tarantool.connect(None, None, socket_fd=sock.fileno(), user="guest") + sock.detach() + try: + self.assertSequenceEqual(conn.eval(self.EVAL_USER), ["guest"]) + finally: + conn.close() + + @unittest.skipIf(sys.platform.startswith("win"), + "Skip on Windows since it uses remote server") + def test_04_tarantool_made_socket(self): + python_exe = find_python() + cwd = os.getcwd() + side_script_path = os.path.join(cwd, "test", "suites", "sidecar.py") + + # pylint: disable=consider-using-f-string + ret_code, err = self.srv.admin(""" + local socket = require('socket') + local popen = require('popen') + local os = require('os') + local s1, s2 = socket.socketpair('AF_UNIX', 'SOCK_STREAM', 0) + + --[[ Tell sidecar which fd use to connect. --]] + os.setenv('SOCKET_FD', tostring(s2:fd())) + + --[[ Tell sidecar where find `tarantool` module. --]] + os.setenv('PYTHONPATH', (os.getenv('PYTHONPATH') or '') .. ':' .. '%s') + + box.session.new({ + type = 'binary', + fd = s1:fd(), + user = 'test', + }) + s1:detach() + + local ph, err = popen.new({'%s', '%s'}, { + stdout = popen.opts.PIPE, + stderr = popen.opts.PIPE, + inherit_fds = {s2:fd()}, + }) + + if err ~= nil then + return 1, err + end + + ph:wait() + + local status_code = ph:info().status.exit_code + local stderr = ph:read({stderr=true}):rstrip() + return status_code, stderr + """ % (cwd, python_exe, side_script_path)) + self.assertIsNone(err, err) + self.assertEqual(ret_code, 0) + + def test_05_socket_fd_pool(self): + sock = self._get_tt_sock() + pool = tarantool.ConnectionPool( + addrs=[{'host': None, 'port': None, 'socket_fd': sock.fileno()}] + ) + sock.detach() + try: + self.assertSequenceEqual(pool.eval(self.EVAL_USER, mode=tarantool.Mode.ANY), ["test"]) + finally: + pool.close() + + def test_06_socket_fd_mesh(self): + sock = self._get_tt_sock() + mesh = tarantool.MeshConnection( + host=None, + port=None, + socket_fd=sock.fileno() + ) + sock.detach() + try: + self.assertSequenceEqual(mesh.eval(self.EVAL_USER), ["test"]) + finally: + mesh.close() + + @classmethod + def tearDownClass(cls): + cls.srv.stop() + cls.srv.clean() From 4a682699edd20fa2888d9bcb4855147334a5fc8e Mon Sep 17 00:00:00 2001 From: Yaroslav Lobankov Date: Thu, 14 Mar 2024 17:09:50 +0400 Subject: [PATCH 176/190] ci: bump actions in reusable_testing.yml Bump version of the `actions/checkout` and `actions/download-artifact` actions to v4. Bump version of the `actions/setup-python` action to v5. It is needed for fixing the following GitHub warning: Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20 --- .github/workflows/reusable_testing.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index e9441532..406a7301 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Clone the tarantool-python connector - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ github.repository_owner }}/tarantool-python - name: Download the tarantool build artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ inputs.artifact_name }} @@ -29,7 +29,7 @@ jobs: run: sudo dpkg -i tarantool*.deb - name: Setup python3 for tests - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' From a12f6e7f8e1b23a8ed2e5076cdcfa130af15a81f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 27 Mar 2024 10:55:00 +0300 Subject: [PATCH 177/190] release 1.2.0 Overview This release introduces socket fd connect support. Added - The ability to connect to the Tarantool using an existing socket fd (#304). --- CHANGELOG.md | 2 +- debian/changelog | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3bd49f1..28f93b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. 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 +## 1.2.0 - 2024-03-27 ### Added - The ability to connect to the Tarantool using an existing socket fd (#304). diff --git a/debian/changelog b/debian/changelog index c8d5153d..07127a11 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +python3-tarantool (1.2.0-0) unstable; urgency=medium + + ## Overview + + This release introduces socket fd connect support. + + ## Added + + - The ability to connect to the Tarantool using an existing socket fd (#304). + + -- Georgy Moiseev Wed, 27 Mar 2024 11:00:00 +0300 + python3-tarantool (1.1.2-0) unstable; urgency=medium ## Overview From 3d397aa86bf71088e91a7d7ee3a74d2583a426b0 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokovskiy Date: Fri, 29 Mar 2024 14:20:07 +0300 Subject: [PATCH 178/190] test: use coverage instead of equality to test error payload We are going to add missing 'user' payload field for ACCESS_DENIED error which will break current tests. Let rewrite tests to allow adding new payload fields for this error. Need for https://github.com/tarantool/tarantool/issues/9108 --- test/suites/test_dml.py | 6 +++--- test/suites/test_error_ext.py | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index f557451e..50b71905 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -419,13 +419,13 @@ def test_16_extra_error_info_fields(self): "Create access to function 'forbidden_function' is denied for user 'test'") self.assertEqual(exc.extra_info.errno, 0) self.assertEqual(exc.extra_info.errcode, 42) - self.assertEqual( - exc.extra_info.fields, + self.assertGreaterEqual( + exc.extra_info.fields.items(), { 'object_type': 'function', 'object_name': 'forbidden_function', 'access_type': 'Create' - }) + }.items()) self.assertEqual(exc.extra_info.prev, None) else: self.fail('Expected error') diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index 52622794..c6cdf4b5 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -327,7 +327,11 @@ def test_tarantool_decode(self): self.assertEqual(err.message, expected_err.message) self.assertEqual(err.errno, expected_err.errno) self.assertEqual(err.errcode, expected_err.errcode) - self.assertEqual(err.fields, expected_err.fields) + if expected_err.fields is not None: + self.assertGreaterEqual(err.fields.items(), + expected_err.fields.items()) + else: + self.assertEqual(err.fields, None) err = err.prev expected_err = expected_err.prev From acc1446a921044be814f68b245c585226ca183a0 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokovskiy Date: Thu, 11 Apr 2024 13:26:19 +0300 Subject: [PATCH 179/190] test: use coverage instead of equality to test error payload p.2 We are going to add 'name' payload field for every client error. So we need to tweak more test to handle this. Need for https://github.com/tarantool/tarantool/issues/9875 --- test/suites/test_dml.py | 14 ++++++++++++-- test/suites/test_error_ext.py | 13 ++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 50b71905..26539eec 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -391,7 +391,12 @@ def test_15_extra_error_info_stacked(self): self.assertEqual(exc.extra_info.message, "Timeout exceeded") self.assertEqual(exc.extra_info.errno, 0) self.assertEqual(exc.extra_info.errcode, 78) - self.assertEqual(exc.extra_info.fields, None) + actual_fields = exc.extra_info.fields + if actual_fields is None: + actual_fields = {} + expected_fields = {} + self.assertGreaterEqual(actual_fields.items(), + expected_fields.items()) self.assertNotEqual(exc.extra_info.prev, None) prev = exc.extra_info.prev self.assertEqual(prev.type, 'ClientError') @@ -400,7 +405,12 @@ def test_15_extra_error_info_stacked(self): self.assertEqual(prev.message, "Unknown error") self.assertEqual(prev.errno, 0) self.assertEqual(prev.errcode, 0) - self.assertEqual(prev.fields, None) + actual_fields = prev.fields + if actual_fields is None: + actual_fields = {} + expected_fields = {} + self.assertGreaterEqual(actual_fields.items(), + expected_fields.items()) else: self.fail('Expected error') diff --git a/test/suites/test_error_ext.py b/test/suites/test_error_ext.py index c6cdf4b5..d5e96e75 100644 --- a/test/suites/test_error_ext.py +++ b/test/suites/test_error_ext.py @@ -327,11 +327,14 @@ def test_tarantool_decode(self): self.assertEqual(err.message, expected_err.message) self.assertEqual(err.errno, expected_err.errno) self.assertEqual(err.errcode, expected_err.errcode) - if expected_err.fields is not None: - self.assertGreaterEqual(err.fields.items(), - expected_err.fields.items()) - else: - self.assertEqual(err.fields, None) + expected_fields = expected_err.fields + if expected_fields is None: + expected_fields = {} + actual_fields = err.fields + if actual_fields is None: + actual_fields = {} + self.assertGreaterEqual(actual_fields.items(), + expected_fields.items()) err = err.prev expected_err = expected_err.prev From 5507a6eb445d4850b7eeb8804cd7ff19ee54401f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 24 May 2024 10:43:49 +0300 Subject: [PATCH 180/190] codespell: ignore unittest naming After resent update, codespell started to treat assertIn as a typo, and CI started to fail. --- .codespellrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codespellrc b/.codespellrc index fe94c2d8..291bd5cf 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] skip = tarantool/msgpack_ext/types/timezones/timezones.py -ignore-words-list = ans,gost,ro +ignore-words-list = ans,gost,ro,assertIn From 717f275f55284c945973ad52d4735ff7aa275104 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 24 May 2024 11:09:08 +0300 Subject: [PATCH 181/190] ci: fix deb build It seems that something has been changed in the package dependencies and `install` no longer works here without `update`. --- .github/workflows/packing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index b5b84f32..c2456477 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -414,6 +414,7 @@ jobs: - name: Install deb packing tools run: | + sudo apt update sudo apt install -y devscripts equivs - name: Make changelog entry for non-release build From e50f7624dea0c0d9ddcd372703e2648037af7133 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 4 Sep 2024 09:56:07 +0300 Subject: [PATCH 182/190] setup: fix import order --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4ee0797c..87bcc4c4 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import codecs import os -from setuptools import setup, find_packages +from setuptools import find_packages, setup from setuptools.command.build_py import build_py # Extra commands for documentation management From 7c5e9a047931553768e5ca1117ff8557a8377b7f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 4 Sep 2024 09:33:02 +0300 Subject: [PATCH 183/190] ci: fix download artifact vulnerability Versions of actions/download-artifact before 4.1.7 are vulnerable to arbitrary file write when downloading and extracting a specifically crafted artifact that contains path traversal filenames [1]. 1. https://github.com/tarantool/tarantool-python/security/dependabot/4 --- .github/workflows/packing.yml | 20 ++++++++++---------- .github/workflows/reusable_testing.yml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index c2456477..c286abf1 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -43,7 +43,7 @@ jobs: run: make pip-dist-check - name: Archive pip artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.0 with: name: pip_dist path: pip_dist @@ -84,7 +84,7 @@ jobs: tarantool-version: '2.11' - name: Download pip package artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: pip_dist path: pip_dist @@ -134,7 +134,7 @@ jobs: run: python3 .github/scripts/remove_source_code.py - name: Download pip package artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: pip_dist path: pip_dist @@ -202,7 +202,7 @@ jobs: run: pip3 install twine - name: Download pip package artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: pip_dist path: pip_dist @@ -271,7 +271,7 @@ jobs: run: make rpm-dist-check - name: Archive RPM artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.0 with: name: rpm_dist_${{ matrix.target.os }}_${{ matrix.target.dist }} path: rpm_dist @@ -324,7 +324,7 @@ jobs: dnf install -y tarantool tarantool-devel - name: Download RPM artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: rpm_dist_${{ matrix.target.os }}_${{ matrix.target.dist }} path: rpm_dist @@ -372,7 +372,7 @@ jobs: run: sudo apt install -y curl make - name: Download RPM artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: rpm_dist_${{ matrix.target.os }}_${{ matrix.target.dist }} path: rpm_dist @@ -433,7 +433,7 @@ jobs: run: make deb-dist-check - name: Archive deb artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.0 with: name: deb_dist path: deb_dist @@ -490,7 +490,7 @@ jobs: DEBIAN_FRONTEND: noninteractive - name: Download deb artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: deb_dist path: deb_dist @@ -542,7 +542,7 @@ jobs: run: sudo apt install -y curl make - name: Download deb artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.8 with: name: deb_dist path: deb_dist diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index 406a7301..aec47845 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -19,7 +19,7 @@ jobs: repository: ${{ github.repository_owner }}/tarantool-python - name: Download the tarantool build artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v4.1.8 with: name: ${{ inputs.artifact_name }} From d291a451865cbd9cd7859dfca0206533f0a1a806 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 4 Sep 2024 09:43:43 +0300 Subject: [PATCH 184/190] ci: cancel previous runs on PR push After this patch, current PR pipeline runs will be cancelled if new commits/force push triggers new pipelines. --- .github/workflows/packing.yml | 4 ++++ .github/workflows/testing.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index c286abf1..c719b31a 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -6,6 +6,10 @@ on: pull_request_target: types: [labeled] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: pack_pip: # We want to run on external PRs, but not on our own internal diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 82f299b4..05927f4c 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -6,6 +6,10 @@ on: pull_request_target: types: [labeled] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: run_tests_ce_linux: # We want to run on external PRs, but not on our own internal From 70c92fdee672b67e9536b36fd5f727c44ae8146f Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 4 Sep 2024 11:08:30 +0300 Subject: [PATCH 185/190] package: drop Python 3.6 support Python 3.6 EOL is 2021-12-23 [1]. Current build script no longer supports Python 3.6 due to `packaging` Python dependency bump. (And fixing the issue is more than just fixating older `packaging` version as a dependency.) https://devguide.python.org/versions/ --- .github/workflows/testing.yml | 9 ++++----- CHANGELOG.md | 5 +++++ requirements.txt | 1 - setup.py | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 05927f4c..b072be05 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -33,7 +33,6 @@ jobs: - '2.11' - 'master' python: - - '3.6' - '3.7' - '3.8' - '3.9' @@ -157,7 +156,7 @@ jobs: path: 'release/linux/x86_64/2.10/' - bundle: 'sdk-gc64-2.11.0-0-r563.linux.x86_64' path: 'release/linux/x86_64/2.11/' - python: ['3.6', '3.11'] + python: ['3.7', '3.11'] steps: - name: Clone the connector @@ -225,7 +224,7 @@ jobs: tarantool: - '2.11' python: - - '3.6' + - '3.7' - '3.11' steps: - name: Clone the connector repo @@ -278,7 +277,7 @@ jobs: tarantool: - '2.11.0.g247a9a418-1' python: - - '3.6' + - '3.7' - '3.11' steps: @@ -348,7 +347,7 @@ jobs: tarantool: - '2.11.0.g247a9a418-1' python: - - '3.6' + - '3.7' - '3.11' steps: - name: Clone the connector repo diff --git a/CHANGELOG.md b/CHANGELOG.md index 28f93b19..8edd47e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. 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 + +### Changed +- Drop Python 3.6 support (PR #327). + ## 1.2.0 - 2024-03-27 ### Added diff --git a/requirements.txt b/requirements.txt index d88dbea3..afcf7b25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ msgpack pytz -dataclasses; python_version <= '3.6' diff --git a/setup.py b/setup.py index 87bcc4c4..3fa65c9c 100755 --- a/setup.py +++ b/setup.py @@ -112,7 +112,7 @@ def get_dependencies(filename): command_options=command_options, install_requires=get_dependencies('requirements.txt'), setup_requires=[ - 'setuptools_scm==6.4.2', + 'setuptools_scm==7.1.0', ], - python_requires='>=3.6', + python_requires='>=3.7', ) From 61e96fcedfef75ee7c4dae4d9bca3c1153e6db85 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Wed, 4 Sep 2024 11:09:53 +0300 Subject: [PATCH 186/190] ci: bump installer script --- .github/workflows/packing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index c719b31a..6613f218 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -324,7 +324,7 @@ jobs: - name: Install tarantool run: | - curl -L https://tarantool.io/yeohchA/release/2/installer.sh | bash + curl -L https://tarantool.io/release/2/installer.sh | bash dnf install -y tarantool tarantool-devel - name: Download RPM artifacts @@ -488,7 +488,7 @@ jobs: - name: Install tarantool ${{ matrix.tarantool }} run: | apt install -y curl - curl -L https://tarantool.io/yeohchA/release/2/installer.sh | bash + curl -L https://tarantool.io/release/2/installer.sh | bash apt install -y tarantool tarantool-dev env: DEBIAN_FRONTEND: noninteractive From 938db75a4d11b33a8e91565fd4f6952249cb7592 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Sun, 22 Sep 2024 19:28:49 +0300 Subject: [PATCH 187/190] pylint: ignore too-many-positional-arguments Pylint 3.3.0 includes a new warning: too-many-positional-arguments [1]. We already ignore a similar too-many-arguments warning in several places since "fixing" it is backward-incompatible. 1. https://github.com/pylint-dev/pylint/issues/9099 --- tarantool/__init__.py | 2 +- tarantool/connection.py | 2 +- tarantool/connection_pool.py | 2 +- tarantool/mesh_connection.py | 2 +- tarantool/request.py | 8 ++++---- test/suites/lib/tarantool_server.py | 2 +- test/suites/test_ssl.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tarantool/__init__.py b/tarantool/__init__.py index d7e99358..97a1e860 100644 --- a/tarantool/__init__.py +++ b/tarantool/__init__.py @@ -1,7 +1,7 @@ """ This package provides API for interaction with a Tarantool server. """ -# pylint: disable=too-many-arguments +# pylint: disable=too-many-arguments,too-many-positional-arguments from tarantool.connection import Connection from tarantool.mesh_connection import MeshConnection diff --git a/tarantool/connection.py b/tarantool/connection.py index 1284fa25..682b0700 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -812,7 +812,7 @@ def __init__(self, .. _mp_bin: https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family .. _mp_array: https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family """ - # pylint: disable=too-many-arguments,too-many-locals,too-many-statements + # pylint: disable=too-many-arguments,too-many-locals,too-many-statements,too-many-positional-arguments if host is None and port is None and socket_fd is None: raise ConfigurationError("need to specify host/port, " diff --git a/tarantool/connection_pool.py b/tarantool/connection_pool.py index 7bbd4e16..f1e675b3 100644 --- a/tarantool/connection_pool.py +++ b/tarantool/connection_pool.py @@ -482,7 +482,7 @@ def __init__(self, .. _box.info.status: .. _box.info: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_info/ """ - # pylint: disable=too-many-arguments,too-many-locals + # pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments if not isinstance(addrs, list) or len(addrs) == 0: raise ConfigurationError("addrs must be non-empty list") diff --git a/tarantool/mesh_connection.py b/tarantool/mesh_connection.py index 0f86c0dd..02a20d7d 100644 --- a/tarantool/mesh_connection.py +++ b/tarantool/mesh_connection.py @@ -463,7 +463,7 @@ def __init__(self, :class:`~tarantool.Connection` exceptions, :class:`~tarantool.MeshConnection.connect` exceptions """ - # pylint: disable=too-many-arguments,too-many-locals + # pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments if addrs is None: addrs = [] diff --git a/tarantool/request.py b/tarantool/request.py index d8d4dd22..95164b79 100644 --- a/tarantool/request.py +++ b/tarantool/request.py @@ -269,7 +269,7 @@ def __init__(self, conn, salt, user, password, auth_type=AUTH_TYPE_CHAP_SHA1): :param auth_type: Refer to :paramref:`~tarantool.Connection.auth_type`. :type auth_type: :obj:`str`, optional """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments super().__init__(conn) @@ -405,7 +405,7 @@ def __init__(self, conn, space_no, index_no, key, offset, limit, iterator): :raise: :exc:`~AssertionError` """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments super().__init__(conn) request_body = self._dumps({IPROTO_SPACE_ID: space_no, @@ -446,7 +446,7 @@ def __init__(self, conn, space_no, index_no, key, op_list): :raise: :exc:`~AssertionError` """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments super().__init__(conn) @@ -569,7 +569,7 @@ def __init__(self, conn, space_no, index_no, tuple_value, op_list): :raise: :exc:`~AssertionError` """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments super().__init__(conn) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index c8f34b37..55f6ca63 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -56,7 +56,7 @@ class TarantoolServer(): """ Class to start up a new Tarantool server. """ - # pylint: disable=too-many-instance-attributes,too-many-arguments,duplicate-code + # pylint: disable=too-many-instance-attributes,too-many-arguments,duplicate-code,too-many-positional-arguments default_tarantool = { "bin": "tarantool", diff --git a/test/suites/test_ssl.py b/test/suites/test_ssl.py index 9452e83b..b7d1da97 100644 --- a/test/suites/test_ssl.py +++ b/test/suites/test_ssl.py @@ -34,7 +34,7 @@ def is_test_ssl(): class SslTestCase: - # pylint: disable=too-few-public-methods,too-many-instance-attributes,too-many-arguments + # pylint: disable=too-few-public-methods,too-many-instance-attributes,too-many-arguments,too-many-positional-arguments def __init__(self, name="", From 1574e4cd8c59c096d0510af418dc8febc59ef923 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Sun, 22 Sep 2024 19:51:59 +0300 Subject: [PATCH 188/190] pylint: ignore unknown-option-value Disabling any new pylint warnings per-entity results in older pylint failing due to unknown option. Since we support several pylint versions due to Python version restrictions, we need to support multiple pylint versions as well. --- .pylintrc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pylintrc b/.pylintrc index 3fd8b942..385d67ad 100644 --- a/.pylintrc +++ b/.pylintrc @@ -6,3 +6,8 @@ good-names=i,j,k,ex,Run,_,ok,t,tz [FORMAT] # Allow links in docstings, allow tables ignore-long-lines=^(?:\s*(# )?(?:\.\.\s.+?:)?\s*?)|(\s\+.+\+)|(\s\|.+\|)$ + +[MESSAGES CONTROL] +# Ignore unknown options to support per-entity ignores for newest warnings/errors +# which are not supported for older versions. +disable=unknown-option-value From b6422af1914db169fbf7f8b074731002e725a3d3 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Sun, 22 Sep 2024 19:41:06 +0300 Subject: [PATCH 189/190] test: fixate linters version strictly Any new linter release, including bugfix ones, may result in failing CI. Since tarantool-python CI is included in several integration pipelines, such things may block work on repos like tarantool/tarantool and tarantool/luajit. On the other hand, if CI will start to fail due to a new linter release, it is unlikely that something user-critical will be discovered. So for now let's stick to the following approach: one will bump linters manually from time to time and fix/disable emerging issues. --- requirements-test.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements-test.txt b/requirements-test.txt index 56f4b595..4d16b8ca 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,8 +1,10 @@ git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-compliance pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' -pylint ~= 3.0 ; python_version >= '3.8' -pylint ~= 2.13 ; python_version < '3.8' -flake8 ~= 6.1 ; python_version >= '3.8' -flake8 ~= 5.0 ; python_version < '3.8' -codespell ~= 2.2 +pylint == 3.3.0 ; python_version >= '3.9' +pylint == 3.2.7 ; python_version == '3.8' +pylint == 2.17.7 ; python_version == '3.7' +flake8 == 6.1.0 ; python_version >= '3.8' +flake8 == 5.0.4 ; python_version < '3.8' +codespell == 2.3.0 ; python_version >= '3.8' +codespell == 2.2.5 ; python_version < '3.8' From 28310fc4e927c77d3be75e98360bf3b09f72482a Mon Sep 17 00:00:00 2001 From: Alexey Potapenko Date: Mon, 31 Mar 2025 20:33:50 +0300 Subject: [PATCH 190/190] ci: bump ubuntu version Bump actions to use ubuntu-24.04 for fixing the following GitHub warning: The Ubuntu 20.04 Actions runner image will begin deprecation on 2025-02-01. Additionally this patch updates test requirements, list of OS to test, forces `xz` compression type to support older OS versions, hot-fixes to solve `CMake` 3.5 problem on latest Ubuntu and Debian. Part of #TNTP-1918 --- .github/workflows/packing.yml | 94 +++++++++++++++++++------- .github/workflows/reusable_testing.yml | 3 +- .github/workflows/testing.yml | 73 ++++++++++---------- .readthedocs.yaml | 3 + debian/control | 2 +- debian/rules | 4 ++ requirements-test.txt | 3 +- 7 files changed, 119 insertions(+), 63 deletions(-) diff --git a/.github/workflows/packing.yml b/.github/workflows/packing.yml index 6613f218..70970387 100644 --- a/.github/workflows/packing.yml +++ b/.github/workflows/packing.yml @@ -20,14 +20,14 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Checkout all tags for correct version computation. with: fetch-depth: 0 @@ -38,7 +38,9 @@ jobs: python-version: '3.11' - name: Install tools for packing and verification - run: pip3 install wheel twine + run: | + pip3 install wheel twine + pip3 install --upgrade setuptools - name: Pack source and binary files run: make pip-dist @@ -65,14 +67,14 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 @@ -83,7 +85,7 @@ jobs: run: python3 .github/scripts/remove_source_code.py - name: Install tarantool - uses: tarantool/setup-tarantool@v2 + uses: tarantool/setup-tarantool@v3 with: tarantool-version: '2.11' @@ -99,10 +101,13 @@ jobs: - name: Install test requirements run: pip3 install -r requirements-test.txt + # Installation of the specific CMake version is a hotfix for + # https://github.com/tarantool/checks/issues/64 - name: Install the crud module for testing purposes run: | curl -L https://tarantool.io/release/2/installer.sh | bash sudo apt install -y tt + pip3 install cmake==3.15.3 tt rocks install crud - name: Run tests @@ -127,7 +132,7 @@ jobs: steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 @@ -188,14 +193,14 @@ jobs: - run_tests_pip_package_linux - run_tests_pip_package_windows - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python and basic packing tools uses: actions/setup-python@v4 @@ -226,7 +231,7 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 container: image: ${{ matrix.target.os }}:${{ matrix.target.dist }} @@ -250,7 +255,7 @@ jobs: run: dnf install -y git - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Checkout all tags for correct version computation. with: fetch-depth: 0 @@ -293,7 +298,7 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 container: image: ${{ matrix.target.os }}:${{ matrix.target.dist }} @@ -312,7 +317,7 @@ jobs: steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python and test running tools # cmake rocks fail to install as expected without findutils: @@ -343,6 +348,7 @@ jobs: run: | curl -L https://tarantool.io/release/2/installer.sh | bash sudo dnf install -y tt + pip3 install cmake==3.15.3 tt rocks install crud - name: Run tests @@ -354,7 +360,7 @@ jobs: needs: - run_tests_rpm - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -370,7 +376,7 @@ jobs: steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install tools for package publishing run: sudo apt install -y curl make @@ -404,14 +410,14 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Checkout all tags for correct version computation with: fetch-depth: 0 @@ -420,6 +426,7 @@ jobs: run: | sudo apt update sudo apt install -y devscripts equivs + sudo apt install python3-setuptools python3-stdeb dh-python - name: Make changelog entry for non-release build if: startsWith(github.ref, 'refs/tags') != true @@ -453,7 +460,7 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 container: image: ${{ matrix.target.os }}:${{ matrix.target.dist }} @@ -463,18 +470,20 @@ jobs: matrix: target: + - os: debian + dist: bullseye # 11 + - os: debian + dist: bookworm # 12 - os: ubuntu dist: focal # 20.04 - os: ubuntu dist: jammy # 22.04 - - os: debian - dist: buster # 10 - - os: debian - dist: bullseye # 11 + - os: ubuntu + dist: noble # 24.04 steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare apt run: apt update @@ -504,17 +513,48 @@ jobs: env: DEBIAN_FRONTEND: noninteractive + # Usage of venv is mandatory starting with Debian 12 and Ubuntu 24.04. + - name: Create venv + run: | + apt install -y python3-venv + python3 -m venv .venv + - name: Install test requirements run: pip3 install -r requirements-test.txt + if: matrix.target.dist != 'bookworm' && matrix.target.dist != 'noble' + + - name: Install test requirements + run: | + . .venv/bin/activate + pip3 install -r requirements-test.txt + if: matrix.target.dist == 'bookworm' || matrix.target.dist == 'noble' - name: Install the crud module for testing purposes run: | curl -L https://tarantool.io/release/2/installer.sh | bash apt install -y tt tt rocks install crud + if: matrix.target.dist != 'bookworm' && matrix.target.dist != 'noble' + + - name: Install the crud module for testing purposes + run: | + . .venv/bin/activate + curl -L https://tarantool.io/release/3/installer.sh | bash + apt install -y tt + tt rocks install crud + if: matrix.target.dist == 'bookworm' || matrix.target.dist == 'noble' - name: Run tests run: make test-pure-install + if: matrix.target.dist != 'bookworm' && matrix.target.dist != 'noble' + + - name: Run tests + run: | + . .venv/bin/activate + export PYTHONPATH=$PYTHONPATH:/usr/lib/python3.11:/usr/lib/python3.12:/usr/bin:/usr/lib/python3/dist-packages + export PATH=$PATH:/usr/lib/python3/dist-packages + make test-pure-install + if: matrix.target.dist == 'bookworm' || matrix.target.dist == 'noble' publish_deb: if: startsWith(github.ref, 'refs/tags') @@ -522,7 +562,7 @@ jobs: needs: - run_tests_deb - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -533,14 +573,18 @@ jobs: dist: focal # 20.04 - os: ubuntu dist: jammy # 22.04 + - os: ubuntu + dist: noble # 24.04 - os: debian dist: buster # 10 - os: debian dist: bullseye # 11 + - os: debian + dist: bookworm # 12 steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install tools for package publishing run: sudo apt install -y curl make diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index aec47845..79f9f2a4 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -11,7 +11,7 @@ on: jobs: run_tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Clone the tarantool-python connector uses: actions/checkout@v4 @@ -43,6 +43,7 @@ jobs: run: | curl -L https://tarantool.io/release/2/installer.sh | bash sudo apt install -y tt + pip3 install cmake==3.15.3 tt rocks install crud - run: make test diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index b072be05..8f94a4e6 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -21,20 +21,16 @@ jobs: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: tarantool: - - '1.10' - - '2.8' - - '2.10' - '2.11' + - '3.3' - 'master' python: - - '3.7' - - '3.8' - '3.9' - '3.10' - '3.11' @@ -47,30 +43,35 @@ jobs: # "This page is taking too long to load." error. Thus we use # pairwise testing. include: - - tarantool: '2.11' + - tarantool: '3.3' python: '3.11' msgpack-deps: 'msgpack==0.5.0' - - tarantool: '2.11' + - tarantool: '3.3' python: '3.11' msgpack-deps: 'msgpack==0.6.2' - - tarantool: '2.11' + - tarantool: '3.3' python: '3.11' msgpack-deps: 'msgpack==1.0.4' steps: - name: Clone the connector - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup tt run: | - curl -L https://tarantool.io/release/2/installer.sh | sudo bash + curl -L https://tarantool.io/release/3/installer.sh | sudo bash sudo apt install -y tt tt version - tt init + tt init + + # Installation of the specific CMake version is a hotfix for + # https://github.com/tarantool/checks/issues/64 + - name: Install old CMake + run: pip3 install cmake==3.15.3 - name: Install tarantool ${{ matrix.tarantool }} if: matrix.tarantool != 'master' - uses: tarantool/setup-tarantool@v2 + uses: tarantool/setup-tarantool@v3 with: tarantool-version: ${{ matrix.tarantool }} @@ -123,8 +124,7 @@ jobs: run: pip3 install -r requirements-test.txt - name: Install the crud module for testing purposes - run: | - tt rocks install crud + run: tt rocks install crud - name: Run tests run: make test @@ -142,34 +142,30 @@ jobs: github.event.pull_request.head.repo.full_name != github.repository && github.event.label.name == 'full-ci') - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: tarantool: - - bundle: 'sdk-1.10.15-0-r563' - path: 'release/linux/x86_64/1.10/' - - bundle: 'sdk-2.8.4-0-r563' - path: 'release/linux/x86_64/2.8/' - - bundle: 'sdk-gc64-2.10.7-0-r563.linux.x86_64' - path: 'release/linux/x86_64/2.10/' - bundle: 'sdk-gc64-2.11.0-0-r563.linux.x86_64' path: 'release/linux/x86_64/2.11/' - python: ['3.7', '3.11'] + - bundle: 'sdk-gc64-3.3.1-0-r55.linux.x86_64' + path: 'release/linux/x86_64/3.3/' + python: ['3.9', '3.11'] steps: - name: Clone the connector # `ref` as merge request is needed for pull_request_target because this # target runs in the context of the base commit of the pull request. - uses: actions/checkout@v3 + uses: actions/checkout@v4 if: github.event_name == 'pull_request_target' with: ref: refs/pull/${{ github.event.pull_request.number }}/merge - name: Clone the connector if: github.event_name != 'pull_request_target' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Tarantool EE SDK run: | @@ -194,8 +190,9 @@ jobs: # See more here: https://github.com/tarantool/tt/issues/282 run: | source tarantool-enterprise/env.sh - curl -L https://tarantool.io/release/2/installer.sh | bash + curl -L https://tarantool.io/release/3/installer.sh | bash sudo apt install -y tt + pip3 install cmake==3.15.3 tt rocks install crud TARANTOOL_DIR=$PWD/tarantool-enterprise - name: Run tests @@ -215,7 +212,7 @@ jobs: if: (github.event_name == 'push') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -223,15 +220,15 @@ jobs: matrix: tarantool: - '2.11' + - '3.3' python: - - '3.7' - '3.11' steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install tarantool ${{ matrix.tarantool }} - uses: tarantool/setup-tarantool@v2 + uses: tarantool/setup-tarantool@v3 with: tarantool-version: ${{ matrix.tarantool }} @@ -246,13 +243,19 @@ jobs: - name: Install the package with pip run: pip3 install git+$GITHUB_SERVER_URL/$GITHUB_REPOSITORY@$GITHUB_REF + # Installation of the specific CMake version is a hotfix for + # https://github.com/tarantool/checks/issues/64 + - name: Install old CMake + run: pip3 install cmake==3.15.3 + - name: Install test requirements run: pip3 install -r requirements-test.txt - name: Install the crud module for testing purposes run: | - curl -L https://tarantool.io/release/2/installer.sh | bash + curl -L https://tarantool.io/release/3/installer.sh | bash sudo apt install -y tt + pip3 install cmake==3.15.3 tt rocks install crud - name: Run tests @@ -275,14 +278,14 @@ jobs: matrix: # Use reduced test matrix cause Windows pipelines are long. tarantool: + # https://github.com/tarantool/tarantool-python/issues/331 - '2.11.0.g247a9a418-1' python: - - '3.7' - '3.11' steps: - name: Clone the connector - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python for tests uses: actions/setup-python@v4 @@ -345,13 +348,13 @@ jobs: matrix: # Use reduced test matrix cause Windows pipelines are long. tarantool: + # https://github.com/tarantool/tarantool-python/issues/331 - '2.11.0.g247a9a418-1' python: - - '3.7' - '3.11' steps: - name: Clone the connector repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python for tests uses: actions/setup-python@v4 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9375a9f6..bc990993 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,3 +13,6 @@ python: - method: pip path: . - requirements: docs/requirements.txt + +sphinx: + configuration: docs/source/conf.py diff --git a/debian/control b/debian/control index dc7869fc..a5cfe113 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Section: python Priority: optional # See https://github.com/astraw/stdeb/issues/175 for dependencies Build-Depends: python3, python3-dev, python3-pip, python3-setuptools, - python3-distutils, python3-wheel, python3-stdeb, dh-python, + python3-wheel, python3-stdeb, dh-python, debhelper (>= 10) Standards-Version: 3.9.1 Homepage: https://github.com/tarantool/tarantool-python diff --git a/debian/rules b/debian/rules index b55a2b66..df1413c3 100755 --- a/debian/rules +++ b/debian/rules @@ -18,3 +18,7 @@ override_dh_auto_install: override_dh_python2: dh_python2 --no-guessing-versions + +# Force `xz` compression for older system with dpkg version < 1.15.6 +override_dh_builddeb: + dh_builddeb -- -Zxz diff --git a/requirements-test.txt b/requirements-test.txt index 4d16b8ca..0f2ad987 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,5 @@ git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-compliance -pyyaml==6.0 +pyyaml >= 6.0.2 importlib-metadata >= 1.0 ; python_version < '3.8' pylint == 3.3.0 ; python_version >= '3.9' pylint == 3.2.7 ; python_version == '3.8' @@ -8,3 +8,4 @@ flake8 == 6.1.0 ; python_version >= '3.8' flake8 == 5.0.4 ; python_version < '3.8' codespell == 2.3.0 ; python_version >= '3.8' codespell == 2.2.5 ; python_version < '3.8' +setuptools >= 75.3.2