diff --git a/synapseclient/client.py b/synapseclient/client.py index 508bd2594..814671415 100644 --- a/synapseclient/client.py +++ b/synapseclient/client.py @@ -93,6 +93,7 @@ id_of, get_properties, MB, + memoize, is_json, extract_synapse_id_from_query, find_data_file_handle, @@ -497,7 +498,7 @@ def login( cached_sessions.set_most_recent_user(self.credentials.username) if not silent: - profile = self.getUserProfile() + profile = self.getUserProfile(refresh=True) # TODO-PY3: in Python2, do we need to ensure that this is encoded in utf-8 self.logger.info( "Welcome, %s!\n" @@ -611,23 +612,26 @@ def invalidateAPIKey(self): if self._is_logged_in(): self.restDELETE("/secretKey", endpoint=self.authEndpoint) - @functools.lru_cache() + @memoize def getUserProfile( self, id: Union[str, int, UserProfile, TeamMember] = None, sessionToken: str = None, + refresh: bool = False, ) -> UserProfile: """ Get the details about a Synapse user. Retrieves information on the current user if 'id' is omitted. :param id: The 'userId' (aka 'ownerId') of a user or the userName :param sessionToken: The session token to use to find the user profile + :param refresh: If set to True will always fetch the data from Synape otherwise will use cached information :returns: The user profile for the user of interest. Example:: my_profile = syn.getUserProfile() freds_profile = syn.getUserProfile('fredcommo') """ + try: # if id is unset or a userID, this will succeed id = "" if id is None else int(id) @@ -648,6 +652,7 @@ def getUserProfile( else: # no break raise ValueError('Can\'t find user "%s": ' % id) uri = "/userProfile/%s" % id + return UserProfile( **self.restGET( uri, headers={"sessionToken": sessionToken} if sessionToken else None @@ -2644,7 +2649,7 @@ def _download_from_URL( else: mode = "wb" previouslyTransferred = 0 - sig = hashlib.new("md5", usedforsecurity=False) + sig = hashlib.md5() try: with open(temp_destination, mode) as fd: @@ -4766,6 +4771,7 @@ def _generate_headers(self, headers=None): if headers is None: headers = dict(self.default_headers) + headers.update(synapseclient.USER_AGENT) return headers @@ -4801,7 +4807,6 @@ def _rest_call( uri, headers = self._build_uri_and_headers( uri, endpoint=endpoint, headers=headers ) - retryPolicy = self._build_retry_policy(retryPolicy) requests_session = requests_session or self._requests_session @@ -4818,7 +4823,6 @@ def _rest_call( verbose=self.debug, **retryPolicy, ) - self._handle_synapse_http_error(response) return response diff --git a/synapseclient/core/utils.py b/synapseclient/core/utils.py index 6b0fbd7b5..d2da9dd5d 100644 --- a/synapseclient/core/utils.py +++ b/synapseclient/core/utils.py @@ -6,6 +6,7 @@ import collections.abc import datetime import errno +import functools import hashlib import importlib import inspect @@ -45,7 +46,7 @@ def md5_for_file(filename, block_size=2 * MB, callback=None): :returns: The MD5 """ - md5 = hashlib.new("md5", usedforsecurity=False) + md5 = hashlib.md5() with open(filename, "rb") as f: while True: if callback: @@ -638,6 +639,21 @@ def extract_synapse_id_from_query(query): raise ValueError('Couldn\'t extract synapse ID from query: "%s"' % query) +# Derived from https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize +def memoize(obj): + cache = obj._memoize_cache = {} + + @functools.wraps(obj) + def memoizer(*args, **kwargs): + refresh = kwargs.pop("refresh", False) + key = str(args) + str(kwargs) + if refresh or key not in cache: + cache[key] = obj(*args, **kwargs) + return cache[key] + + return memoizer + + def printTransferProgress( transferred, toBeTransferred, diff --git a/tests/unit/synapseclient/core/unit_test_utils.py b/tests/unit/synapseclient/core/unit_test_utils.py index aa7fdc2dc..09187169f 100644 --- a/tests/unit/synapseclient/core/unit_test_utils.py +++ b/tests/unit/synapseclient/core/unit_test_utils.py @@ -483,7 +483,7 @@ def test_md5_for_file(mock_hashlib): file_name = "/home/foo/bar/test.txt" mock_callback = Mock() mock_md5 = Mock() - mock_hashlib.new.return_value = mock_md5 + mock_hashlib.md5.return_value = mock_md5 with patch.object(utils, "open", mock_open(), create=True) as mocked_open: mocked_open.return_value.read.side_effect = ["data1", "data2", None] utils.md5_for_file(file_name, callback=mock_callback) diff --git a/tests/unit/synapseclient/unit_test_client.py b/tests/unit/synapseclient/unit_test_client.py index deb9f0cf4..42547d642 100644 --- a/tests/unit/synapseclient/unit_test_client.py +++ b/tests/unit/synapseclient/unit_test_client.py @@ -141,6 +141,7 @@ def test_login__silentIsFalse(self): # method under test self.syn.login(silent=False, **self.login_args) + mocked_get_user_profile.assert_called_once_with(refresh=True) mocked_logger.info.assert_called_once() def test_login__rememberMeIsTrue(self, mocker):