diff --git a/src/tribler-common/tribler_common/simpledefs.py b/src/tribler-common/tribler_common/simpledefs.py index 95ff5bea2c2..b2f74564fa8 100644 --- a/src/tribler-common/tribler_common/simpledefs.py +++ b/src/tribler-common/tribler_common/simpledefs.py @@ -110,3 +110,11 @@ class CHANNEL_STATE(Enum): DOWNLOADING = "Downloading" PREVIEW = "Preview" METAINFO_LOOKUP = "Searching for metainfo" + + +# Max download or upload rate limit for libtorrent. +# On Win64, the compiled version of libtorrent only supported 2^31 - 1 +# as rate limit values instead of sys.maxsize or 2^63 -1. Since 2^31 +# is a sufficiently large value for download/upload rate limit, +# here we set the max values for these parameters. +MAX_LIBTORRENT_RATE_LIMIT = 2 ** 31 - 1 # bytes per second diff --git a/src/tribler-core/tribler_core/config/test_tribler_config.py b/src/tribler-core/tribler_core/config/test_tribler_config.py index c21f307a16d..789c5e27270 100644 --- a/src/tribler-core/tribler_core/config/test_tribler_config.py +++ b/src/tribler-core/tribler_core/config/test_tribler_config.py @@ -1,5 +1,7 @@ from pathlib import Path +from tribler_common.simpledefs import MAX_LIBTORRENT_RATE_LIMIT + from tribler_core.config.tribler_config import CONFIG_FILENAME, TriblerConfig from tribler_core.utilities.osutils import get_home_dir @@ -191,6 +193,19 @@ def test_get_set_methods_libtorrent(tribler_config): tribler_config.set_libtorrent_dht_enabled(False) assert not tribler_config.get_libtorrent_dht_enabled() + # Add tests for setting libtorrent rate limits + rate_limit = MAX_LIBTORRENT_RATE_LIMIT - 1024 # lower than the max value set + tribler_config.set_libtorrent_max_upload_rate(rate_limit) + assert tribler_config.get_libtorrent_max_upload_rate() == rate_limit + tribler_config.set_libtorrent_max_download_rate(rate_limit) + assert tribler_config.get_libtorrent_max_download_rate() == rate_limit + + rate_limit = MAX_LIBTORRENT_RATE_LIMIT + 1024 # higher than the max value set + tribler_config.set_libtorrent_max_upload_rate(rate_limit) + assert tribler_config.get_libtorrent_max_upload_rate() == MAX_LIBTORRENT_RATE_LIMIT + tribler_config.set_libtorrent_max_download_rate(rate_limit) + assert tribler_config.get_libtorrent_max_download_rate() == MAX_LIBTORRENT_RATE_LIMIT + def test_get_set_methods_tunnel_community(tribler_config): """ diff --git a/src/tribler-core/tribler_core/config/tribler_config.py b/src/tribler-core/tribler_core/config/tribler_config.py index 774e8fe481f..d9fe12d6198 100644 --- a/src/tribler-core/tribler_core/config/tribler_config.py +++ b/src/tribler-core/tribler_core/config/tribler_config.py @@ -8,6 +8,8 @@ from validate import Validator +from tribler_common.simpledefs import MAX_LIBTORRENT_RATE_LIMIT + from tribler_core.exceptions import InvalidConfigException from tribler_core.modules.libtorrent.download_config import get_default_dest_dir from tribler_core.utilities import path_util @@ -442,7 +444,7 @@ def get_libtorrent_max_upload_rate(self): :return: the maximum upload rate in kB / s """ - return self.config['libtorrent'].as_int('max_upload_rate') + return min(self.config['libtorrent'].as_int('max_upload_rate'), MAX_LIBTORRENT_RATE_LIMIT) def set_libtorrent_max_download_rate(self, value): """ @@ -459,7 +461,7 @@ def get_libtorrent_max_download_rate(self): :return: the maximum download rate in kB / s """ - return self.config['libtorrent'].as_int('max_download_rate') + return min(self.config['libtorrent'].as_int('max_download_rate'), MAX_LIBTORRENT_RATE_LIMIT) def set_libtorrent_dht_enabled(self, value): self.config['libtorrent']['dht'] = value diff --git a/src/tribler-core/tribler_core/modules/ipv8_module_catalog.py b/src/tribler-core/tribler_core/modules/ipv8_module_catalog.py index 73e65e3268d..9770f7b4af1 100644 --- a/src/tribler-core/tribler_core/modules/ipv8_module_catalog.py +++ b/src/tribler-core/tribler_core/modules/ipv8_module_catalog.py @@ -122,7 +122,8 @@ class GigaChannelTestnetCommunityLauncher(TestnetMixIn, GigaChannelCommunityLaun @set_in_session('remote_query_community') @overlay('tribler_core.modules.metadata_store.community.remote_query_community', 'RemoteQueryCommunity') @kwargs(metadata_store='session.mds', notifier='session.notifier') -@walk_strategy('ipv8.peerdiscovery.discovery', 'RandomWalk', target_peers=50) +@walk_strategy('ipv8.peerdiscovery.discovery', 'RandomWalk', target_peers=30) +@walk_strategy('tribler_core.modules.metadata_store.community.sync_strategy', 'RemovePeers', target_peers=-1) class RemoteQueryCommunityLauncher(IPv8CommunityLauncher): pass diff --git a/src/tribler-core/tribler_core/modules/metadata_store/community/remote_query_community.py b/src/tribler-core/tribler_core/modules/metadata_store/community/remote_query_community.py index 894a59c435c..b7e301d4fa6 100644 --- a/src/tribler-core/tribler_core/modules/metadata_store/community/remote_query_community.py +++ b/src/tribler-core/tribler_core/modules/metadata_store/community/remote_query_community.py @@ -5,8 +5,11 @@ from ipv8.community import Community from ipv8.lazy_community import lazy_wrapper from ipv8.messaging.lazy_payload import VariablePayload, vp_compile +from ipv8.peerdiscovery.network import Network from ipv8.requestcache import RandomNumberCache, RequestCache +from pony.orm.dbapiprovider import OperationalError + from tribler_common.simpledefs import CHANNELS_VIEW_UUID, NTFY from tribler_core.modules.metadata_store.orm_bindings.channel_metadata import entries_to_chunk @@ -73,10 +76,9 @@ class RemoteQueryCommunity(Community): community_id = unhexlify('dc43e3465cbd83948f30d3d3e8336d71cce33aa7') def __init__(self, my_peer, endpoint, network, metadata_store, settings=None, notifier=None): - super(RemoteQueryCommunity, self).__init__(my_peer, endpoint, network) + super().__init__(my_peer, endpoint, Network()) self.notifier = notifier - self.max_peers = 60 self.settings = settings or RemoteQueryCommunitySettings() @@ -90,8 +92,7 @@ def __init__(self, my_peer, endpoint, network, metadata_store, settings=None, no self.queried_peers_limit = 1000 if self.notifier: - self.notifier.add_observer(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, - self.on_pc_add_unknown_torrent) + self.notifier.add_observer(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, self.on_pc_add_unknown_torrent) # this flag enable or disable https://github.com/Tribler/tribler/pull/5657 # it can be changed in runtime @@ -133,17 +134,30 @@ def send_remote_select_subscribed_channels(self, peer): } self.send_remote_select(peer, **request_dict) + async def process_rpc_query(self, json_bytes: bytes): + """ + Retrieve the result of a database query from a third party, encoded as raw JSON bytes (through `dumps`). + + :raises TypeError: if the JSON contains invalid keys. + :raises ValueError: if no JSON could be decoded. + :raises pony.orm.dbapiprovider.OperationalError: if an illegal query was performed. + """ + request_sanitized = sanitize_query(json.loads(json_bytes), self.settings.max_response_size) + return await self.mds.MetadataNode.get_entries_threaded(**request_sanitized) + @lazy_wrapper(RemoteSelectPayload) async def on_remote_select(self, peer, request): - request_sanitized = sanitize_query(json.loads(request.json), self.settings.max_response_size) - db_results = await self.mds.MetadataNode.get_entries_threaded(**request_sanitized) - if not db_results: - return - - index = 0 - while index < len(db_results): - data, index = entries_to_chunk(db_results, self.settings.maximum_payload_size, start_index=index) - self.ez_send(peer, SelectResponsePayload(request.id, data)) + try: + db_results = await self.process_rpc_query(request.json) + if not db_results: + return + + index = 0 + while index < len(db_results): + data, index = entries_to_chunk(db_results, self.settings.maximum_payload_size, start_index=index) + self.ez_send(peer, SelectResponsePayload(request.id, data)) + except (OperationalError, TypeError, ValueError) as error: + self.logger.error(f"Remote select. The error occurred: {error}") @lazy_wrapper(SelectResponsePayload) async def on_remote_select_response(self, peer, response): @@ -190,8 +204,7 @@ async def unload(self): await super(RemoteQueryCommunity, self).unload() if self.notifier: - self.notifier.remove_observer(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, - self.on_pc_add_unknown_torrent) + self.notifier.remove_observer(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, self.on_pc_add_unknown_torrent) class RemoteQueryTestnetCommunity(RemoteQueryCommunity): diff --git a/src/tribler-core/tribler_core/modules/metadata_store/community/sync_strategy.py b/src/tribler-core/tribler_core/modules/metadata_store/community/sync_strategy.py index 64e22678031..969ca998770 100644 --- a/src/tribler-core/tribler_core/modules/metadata_store/community/sync_strategy.py +++ b/src/tribler-core/tribler_core/modules/metadata_store/community/sync_strategy.py @@ -17,3 +17,17 @@ def take_step(self): if peers: peer = choice(peers) self.overlay.send_random_to(peer) + + +class RemovePeers(DiscoveryStrategy): + """ + Synchronization strategy for remote query community. + + Remove a random peer, if we have enough peers to walk to. + """ + + def take_step(self): + with self.walk_lock: + peers = self.overlay.get_peers() + if peers and len(peers) > 20: + self.overlay.network.remove_peer(choice(peers)) diff --git a/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_remote_query_community.py b/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_remote_query_community.py index 09c3f1358f0..2bebf570e26 100644 --- a/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_remote_query_community.py +++ b/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_remote_query_community.py @@ -1,8 +1,12 @@ +from datetime import datetime +from json import dumps + from ipv8.keyvault.crypto import default_eccrypto from ipv8.peer import Peer from ipv8.test.base import TestBase from pony.orm import db_session +from pony.orm.dbapiprovider import OperationalError from tribler_common.simpledefs import NTFY @@ -33,6 +37,7 @@ def setUp(self): super(TestRemoteQueryCommunity, self).setUp() self.count = 0 self.initialize(RemoteQueryCommunity, 2) + self.torrent_template = {"title": "", "infohash": b"", "torrent_date": datetime(1970, 1, 1), "tags": "video"} def create_node(self, *args, **kwargs): metadata_store = MetadataStore( @@ -47,6 +52,15 @@ def create_node(self, *args, **kwargs): self.count += 1 return node + def channel_metadata(self, i): + return self.nodes[i].overlay.mds.ChannelMetadata + + def torrent_metadata(self, i): + return self.nodes[i].overlay.mds.TorrentMetadata + + def overlay(self, i): + return self.nodes[i].overlay + async def test_remote_select(self): # Fill Node 0 DB with channels and torrents entries with db_session: @@ -235,6 +249,7 @@ def has_testing_infohash(t): assert torrent_has_been_added_to_db1 assert torrent_not_presented_on_db2 + await self.introduce_nodes() remote_query = {"infohash": hexlify(torrent_infohash)} self.nodes[1].overlay.send_remote_select_to_many(**remote_query) @@ -273,9 +288,7 @@ def has_testing_infohash(t): assert torrent_not_presented_on_db2 # notify second node that new torrent hash has been received from the first node - rqc2.notifier.notify(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, - self.nodes[0].my_peer, - torrent_infohash) + rqc2.notifier.notify(NTFY.POPULARITY_COMMUNITY_ADD_UNKNOWN_TORRENT, self.nodes[0].my_peer, torrent_infohash) await self.deliver_messages(timeout=0.5) with db_session: @@ -298,9 +311,81 @@ async def test_unknown_query_attribute(self): await self.deliver_messages(timeout=0.1) # mixed: the old and a new attribute - rqc_node2.send_remote_select_to_many(**{'infohash': hexlify(b'0' * 20), - 'new_attribute': 'some_value'}) + rqc_node2.send_remote_select_to_many(**{'infohash': hexlify(b'0' * 20), 'new_attribute': 'some_value'}) await self.deliver_messages(timeout=0.1) # no exception have been raised assert True + + async def test_process_rpc_query_match_many(self): + """ + Check if a correct query with a match in our database returns a result. + """ + with db_session: + channel = self.channel_metadata(0).create_channel("a channel", "") + add_random_torrent(self.torrent_metadata(0), name="a torrent", channel=channel) + + results = await self.overlay(0).process_rpc_query(dumps({})) + self.assertEqual(2, len(results)) + + channel_md, torrent_md = results if isinstance(results[0], self.channel_metadata(0)) else results[::-1] + self.assertEqual("a channel", channel_md.title) + self.assertEqual("a torrent", torrent_md.title) + + async def test_process_rpc_query_match_one(self): + """ + Check if a correct query with one match in our database returns one result. + """ + with db_session: + self.channel_metadata(0).create_channel("a channel", "") + + results = await self.overlay(0).process_rpc_query(dumps({})) + self.assertEqual(1, len(results)) + + channel_md, = results + self.assertEqual("a channel", channel_md.title) + + async def test_process_rpc_query_match_none(self): + """ + Check if a correct query with no match in our database returns no result. + """ + results = await self.overlay(0).process_rpc_query(dumps({})) + self.assertEqual(0, len(results)) + + async def test_process_rpc_query_match_empty_json(self): + """ + Check if processing an empty request causes a ValueError (JSONDecodeError) to be raised. + """ + with self.assertRaises(ValueError): + await self.overlay(0).process_rpc_query(b'') + + async def test_process_rpc_query_match_illegal_json(self): + """ + Check if processing a request with illegal JSON causes a UnicodeDecodeError to be raised. + """ + with self.assertRaises(UnicodeDecodeError): + await self.overlay(0).process_rpc_query(b'{"akey":\x80}') + + async def test_process_rpc_query_match_invalid_json(self): + """ + Check if processing a request with invalid JSON causes a ValueError to be raised. + """ + with db_session: + self.channel_metadata(0).create_channel("a channel", "") + query = b'{"id_":' + b'\x31' * 200 + b'}' + with self.assertRaises(ValueError): + await self.overlay(0).process_rpc_query(query) + + async def test_process_rpc_query_match_invalid_key(self): + """ + Check if processing a request with invalid flags causes a UnicodeDecodeError to be raised. + """ + with self.assertRaises(TypeError): + await self.overlay(0).process_rpc_query(b'{"bla":":("}') + + async def test_process_rpc_query_no_column(self): + """ + Check if processing a request with no database columns causes an OperationalError. + """ + with self.assertRaises(OperationalError): + await self.overlay(0).process_rpc_query(b'{"txt_filter":{"key":"bla"}}') diff --git a/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_sync_strategy.py b/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_sync_strategy.py index 70444a0c7f5..1ec7b13ba99 100644 --- a/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_sync_strategy.py +++ b/src/tribler-core/tribler_core/modules/metadata_store/community/tests/test_sync_strategy.py @@ -1,9 +1,11 @@ from ipv8.keyvault.crypto import default_eccrypto from ipv8.peer import Peer +from ipv8.peerdiscovery.network import Network +from ipv8.test.base import TestBase import pytest -from tribler_core.modules.metadata_store.community.sync_strategy import SyncChannels +from tribler_core.modules.metadata_store.community.sync_strategy import RemovePeers, SyncChannels class MockCommunity(object): @@ -11,6 +13,7 @@ def __init__(self): self.fetch_next_called = False self.send_random_to_called = [] self.get_peers_return = [] + self.network = Network() def send_random_to(self, peer): self.send_random_to_called.append(peer) @@ -65,3 +68,43 @@ def test_strategy_multi_peer(mock_community, strategy): assert len(mock_community.send_random_to_called) == 1 assert mock_community.send_random_to_called[0] in mock_community.get_peers_return + + +class TestRemovePeers(TestBase): + def setUp(self): + self.community = MockCommunity() + self.strategy = RemovePeers(self.community) + return super().setUp() + + def test_strategy_no_peers(self): + """ + If we have no peers, nothing should happen. + """ + self.strategy.take_step() + + self.assertSetEqual(set(), self.community.network.verified_peers) + + def test_strategy_one_peer(self): + """ + If we have one peer, it should not be removed. + """ + test_peer = Peer(default_eccrypto.generate_key(u"very-low")) + self.community.network.add_verified_peer(test_peer) + self.community.get_peers_return.append(test_peer) + + self.strategy.take_step() + + self.assertSetEqual({test_peer}, self.community.network.verified_peers) + + def test_strategy_multi_peer(self): + """ + If we have over 20 peers, one should be removed. + """ + for _ in range(21): + test_peer = Peer(default_eccrypto.generate_key(u"very-low")) + self.community.network.add_verified_peer(test_peer) + self.community.get_peers_return.append(test_peer) + + self.strategy.take_step() + + self.assertEqual(20, len(self.community.network.verified_peers)) diff --git a/src/tribler-core/tribler_core/modules/tunnel/community/triblertunnel_community.py b/src/tribler-core/tribler_core/modules/tunnel/community/triblertunnel_community.py index 8a3bb3ad476..80a90280609 100644 --- a/src/tribler-core/tribler_core/modules/tunnel/community/triblertunnel_community.py +++ b/src/tribler-core/tribler_core/modules/tunnel/community/triblertunnel_community.py @@ -12,10 +12,7 @@ from ipv8.messaging.anonymization.caches import CreateRequestCache from ipv8.messaging.anonymization.community import unpack_cell from ipv8.messaging.anonymization.hidden_services import HiddenTunnelCommunity -from ipv8.messaging.anonymization.payload import ( - EstablishIntroPayload, - NO_CRYPTO_PACKETS -) +from ipv8.messaging.anonymization.payload import EstablishIntroPayload, NO_CRYPTO_PACKETS from ipv8.messaging.anonymization.tunnel import ( CIRCUIT_STATE_CLOSING, CIRCUIT_STATE_READY, @@ -26,7 +23,7 @@ EXIT_NODE, PEER_FLAG_EXIT_BT, PEER_FLAG_EXIT_IPV8, - RelayRoute + RelayRoute, ) from ipv8.peer import Peer from ipv8.peerdiscovery.network import Network @@ -49,7 +46,7 @@ HTTPRequestPayload, HTTPResponsePayload, RelayBalanceRequestPayload, - RelayBalanceResponsePayload + RelayBalanceResponsePayload, ) from tribler_core.modules.tunnel.socks5.server import Socks5Server from tribler_core.utilities import path_util diff --git a/src/tribler-core/tribler_core/modules/tunnel/test_full_session/test_tunnel_community.py b/src/tribler-core/tribler_core/modules/tunnel/test_full_session/test_tunnel_community.py index ec27b73bc1a..9120f479f14 100644 --- a/src/tribler-core/tribler_core/modules/tunnel/test_full_session/test_tunnel_community.py +++ b/src/tribler-core/tribler_core/modules/tunnel/test_full_session/test_tunnel_community.py @@ -10,7 +10,6 @@ from ipv8.test.messaging.anonymization.test_community import MockDHTProvider from ipv8.test.mocking.exit_socket import MockTunnelExitSocket from ipv8.test.mocking.ipv8 import MockIPv8 -from ipv8.util import succeed import pytest @@ -22,7 +21,6 @@ from tribler_core.session import Session from tribler_core.tests.tools.common import TESTS_DATA_DIR - # Pylint does not agree with the way pytest handles fixtures. # pylint: disable=W0613,W0621 diff --git a/src/tribler-core/tribler_core/modules/versioncheck_manager.py b/src/tribler-core/tribler_core/modules/versioncheck_manager.py index 756a156c031..76f8e75bafb 100644 --- a/src/tribler-core/tribler_core/modules/versioncheck_manager.py +++ b/src/tribler-core/tribler_core/modules/versioncheck_manager.py @@ -2,8 +2,14 @@ import logging from distutils.version import LooseVersion -from aiohttp import ClientConnectionError, ClientResponseError, ClientSession, ClientTimeout, \ - ContentTypeError, ServerConnectionError +from aiohttp import ( + ClientConnectionError, + ClientResponseError, + ClientSession, + ClientTimeout, + ContentTypeError, + ServerConnectionError, +) from ipv8.taskmanager import TaskManager @@ -46,6 +52,11 @@ async def check_new_version_api(self, version_check_url): async with ClientSession(raise_for_status=True) as session: response = await session.get(version_check_url, timeout=ClientTimeout(total=VERSION_CHECK_TIMEOUT)) response_dict = await response.json(content_type=None) + version = response_dict['name'][1:] + if LooseVersion(version) > LooseVersion(version_id): + self.session.notifier.notify(NTFY.TRIBLER_NEW_VERSION, version) + return True + return False except (ServerConnectionError, ClientConnectionError) as e: self._logger.error("Error when performing version check request: %s", e) return False @@ -58,12 +69,5 @@ async def check_new_version_api(self, version_check_url): except asyncio.TimeoutError: self._logger.warning("Checking for new version failed for %s", version_check_url) return False - - try: - version = response_dict['name'][1:] - if LooseVersion(version) > LooseVersion(version_id): - self.session.notifier.notify(NTFY.TRIBLER_NEW_VERSION, version) - return True - return False except ValueError as ve: raise ValueError("Failed to parse Tribler version response.\nError:%s" % ve) diff --git a/src/tribler-core/tribler_core/restapi/tests/test_settings_endpoint.py b/src/tribler-core/tribler_core/restapi/tests/test_settings_endpoint.py index a949d37fd19..5892372cfda 100644 --- a/src/tribler-core/tribler_core/restapi/tests/test_settings_endpoint.py +++ b/src/tribler-core/tribler_core/restapi/tests/test_settings_endpoint.py @@ -3,6 +3,8 @@ import pytest +from tribler_common.simpledefs import MAX_LIBTORRENT_RATE_LIMIT + from tribler_core.modules.libtorrent.download_config import DownloadConfig from tribler_core.restapi.base_api_test import do_request @@ -87,3 +89,27 @@ async def test_set_settings(enable_api, mock_dlmgr, session): assert session.config.get_seeding_mode() == 'ratio' assert session.config.get_seeding_ratio() == 3 assert session.config.get_seeding_time() == 123 + + +@pytest.mark.asyncio +async def test_set_rate_settings(enable_api, mock_dlmgr, session): + """ + Testing whether libtorrent rate limits works for large number without overflow error. + """ + + dcfg = DownloadConfig() + download = Mock() + download.config = dcfg + session.dlmgr.get_downloads = lambda: [download] + + extra_rate = 1024 * 1024 * 1024 # 1GB/s + post_data = { + 'libtorrent': { + 'max_download_rate': MAX_LIBTORRENT_RATE_LIMIT + extra_rate, + 'max_upload_rate': MAX_LIBTORRENT_RATE_LIMIT + extra_rate + } + } + await do_request(session, 'settings', expected_code=200, request_type='POST', post_data=post_data) + + assert session.config.get_libtorrent_max_download_rate() == MAX_LIBTORRENT_RATE_LIMIT + assert session.config.get_libtorrent_max_upload_rate() == MAX_LIBTORRENT_RATE_LIMIT diff --git a/src/tribler-core/tribler_core/session.py b/src/tribler-core/tribler_core/session.py index ed26f4bf22e..7d30f0d40b2 100644 --- a/src/tribler-core/tribler_core/session.py +++ b/src/tribler-core/tribler_core/session.py @@ -151,7 +151,7 @@ def enable_ipv8_statistics(self): def import_bootstrap_file(self): with open(self.bootstrap.bootstrap_file, 'r') as f: - sql_dumb = f.read() + f.read() self._logger.info("Executing bootstrap script") # TODO we should do something here... diff --git a/src/tribler-gui/tribler_gui/dialogs/addtopersonalchanneldialog.py b/src/tribler-gui/tribler_gui/dialogs/addtopersonalchanneldialog.py index a3ad58eda37..de9ba01e7d0 100644 --- a/src/tribler-gui/tribler_gui/dialogs/addtopersonalchanneldialog.py +++ b/src/tribler-gui/tribler_gui/dialogs/addtopersonalchanneldialog.py @@ -133,9 +133,9 @@ def on_channel_contents(self, response, channel_id): return # No results means this node is a leaf - self.channels_tree[channel_id] = set() if response["results"] else None + self.channels_tree[channel_id] = set() if response.get("results") else None - for subchannel in response["results"]: + for subchannel in response.get("results", []): subchannel_id = subchannel["id"] if subchannel_id in self.id2wt_mapping: continue diff --git a/src/tribler-gui/tribler_gui/tribler_window.py b/src/tribler-gui/tribler_gui/tribler_window.py index 6bde5a3c89f..f0bc0a550fe 100644 --- a/src/tribler-gui/tribler_gui/tribler_window.py +++ b/src/tribler-gui/tribler_gui/tribler_window.py @@ -573,6 +573,12 @@ def on_new_version_available(self, version): if version == str(self.gui_settings.value('last_reported_version')): return + # To prevent multiple dialogs on top of each other, + # close any existing dialog first. + if self.new_version_dialog: + self.new_version_dialog.close_dialog() + self.new_version_dialog = None + self.new_version_dialog = ConfirmationDialog( self, "New version available", diff --git a/src/tribler-gui/tribler_gui/widgets/settingspage.py b/src/tribler-gui/tribler_gui/widgets/settingspage.py index c82180f4127..567a9b55f91 100644 --- a/src/tribler-gui/tribler_gui/widgets/settingspage.py +++ b/src/tribler-gui/tribler_gui/widgets/settingspage.py @@ -1,10 +1,10 @@ -import sys - from PIL.ImageQt import ImageQt from PyQt5 import QtCore, QtGui from PyQt5.QtWidgets import QFileDialog, QLabel, QSizePolicy, QWidget +from tribler_common.simpledefs import MAX_LIBTORRENT_RATE_LIMIT + import tribler_core.utilities.json_util as json from tribler_gui.defs import ( @@ -343,14 +343,14 @@ def save_settings(self): try: if self.window().upload_rate_limit_input.text(): - user_upload_rate_limit = int(self.window().upload_rate_limit_input.text()) * 1024 - if user_upload_rate_limit < sys.maxsize: + user_upload_rate_limit = int(float(self.window().upload_rate_limit_input.text()) * 1024) + if user_upload_rate_limit < MAX_LIBTORRENT_RATE_LIMIT: settings_data['libtorrent']['max_upload_rate'] = user_upload_rate_limit else: raise ValueError if self.window().download_rate_limit_input.text(): - user_download_rate_limit = int(self.window().download_rate_limit_input.text()) * 1024 - if user_download_rate_limit < sys.maxsize: + user_download_rate_limit = int(float(self.window().download_rate_limit_input.text()) * 1024) + if user_download_rate_limit < MAX_LIBTORRENT_RATE_LIMIT: settings_data['libtorrent']['max_download_rate'] = user_download_rate_limit else: raise ValueError @@ -358,8 +358,9 @@ def save_settings(self): ConfirmationDialog.show_error( self.window(), "Invalid value for bandwidth limit", - "You've entered an invalid value for the maximum upload/download rate. " - "Please enter a whole number (max: %d)" % (sys.maxsize / 1000), + "You've entered an invalid value for the maximum upload/download rate. \n" + "The rate is specified in KB/s and the value permitted is between 0 and %d KB/s.\n" + "Note that the decimal values are truncated." % (MAX_LIBTORRENT_RATE_LIMIT / 1024), ) return