Repository: stravalib/stravalib
Pull request #241 (2022-10-31 10:46:21)

Improved unknown time zone handling
Some activities have unknown or incorrect time zones in the JSON response from Strava, e.g., '(GMT+00:00) Factory'. This change will make the unmarshaller from `TimezoneAttribute` return `None` instead of raising an `UnknowTimeZoneError` (and crashing the overall unmarshalling process).


Patch:
diff --git a/docs/reference/api/stravalib.client.Client.get_running_race.rst b/docs/reference/api/stravalib.client.Client.get_running_race.rst
deleted file mode 100644
index 1e500f6..0000000
--- a/docs/reference/api/stravalib.client.Client.get_running_race.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-﻿stravalib.client.Client.get\_running\_race
-==========================================
-
-.. currentmodule:: stravalib.client
-
-.. automethod:: Client.get_running_race
\ No newline at end of file
diff --git a/docs/reference/api/stravalib.client.Client.get_running_races.rst b/docs/reference/api/stravalib.client.Client.get_running_races.rst
deleted file mode 100644
index 122a4d4..0000000
--- a/docs/reference/api/stravalib.client.Client.get_running_races.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-﻿stravalib.client.Client.get\_running\_races
-===========================================
-
-.. currentmodule:: stravalib.client
-
-.. automethod:: Client.get_running_races
\ No newline at end of file
diff --git a/docs/reference/api/stravalib.client.Client.rst b/docs/reference/api/stravalib.client.Client.rst
index cd5ea13..8759fad 100644
--- a/docs/reference/api/stravalib.client.Client.rst
+++ b/docs/reference/api/stravalib.client.Client.rst
@@ -48,8 +48,6 @@
       ~Client.get_route
       ~Client.get_route_streams
       ~Client.get_routes
-      ~Client.get_running_race
-      ~Client.get_running_races
       ~Client.get_segment
       ~Client.get_segment_effort
       ~Client.get_segment_efforts
diff --git a/docs/reference/client.rst b/docs/reference/client.rst
index fa42fde..168d258 100644
--- a/docs/reference/client.rst
+++ b/docs/reference/client.rst
@@ -94,8 +94,6 @@ Stream related methods
    Client.get_activity_streams
    Client.get_effort_streams
    Client.get_segment_streams
-   Client.get_running_race
-   Client.get_running_races
 
 Route related methods
 ----------------------
diff --git a/stravalib/attributes.py b/stravalib/attributes.py
index ae80460..6044cf4 100644
--- a/stravalib/attributes.py
+++ b/stravalib/attributes.py
@@ -14,6 +14,7 @@ from weakref import WeakKeyDictionary, WeakValueDictionary
 
 import arrow
 import pytz
+from pytz.exceptions import UnknownTimeZoneError
 from units.quantity import Quantity
 import six
 
@@ -206,7 +207,11 @@ class TimezoneAttribute(Attribute):
             else:
                 # America/Los_Angeles
                 tzname = v
-            v = pytz.timezone(tzname)
+            try:
+                v = pytz.timezone(tzname)
+            except UnknownTimeZoneError as e:
+                self.log.warning(f'Encountered unknown time zone {tzname}, returning None')
+                v = None
         return v
 
     def marshal(self, v):
diff --git a/stravalib/client.py b/stravalib/client.py
index 55ed00d..cf17c4b 100644
--- a/stravalib/client.py
+++ b/stravalib/client.py
@@ -1374,48 +1374,6 @@ class Client(object):
         # Pack streams into dictionary
         return {i.type: i for i in streams}
 
-    def get_running_race(self, race_id):
-        """
-        Gets a running race for a given identifier.
-
-        TODO: This has been deprecated by strava as of Nov 1 2021. See
-        https://developers.strava.com/docs/changelog/
-        https://developers.strava.com/docs/reference/#api-models-RunningRace
-
-        :param race_id: id for the race
-
-        :rtype: :class:`stravalib.model.RunningRace`
-        """
-        raw = self.protocol.get('/running_races/{id}', id=race_id)
-        return model.RunningRace.deserialize(raw, bind_client=self)
-
-
-    def get_running_races(self, year=None):
-        """
-        Gets a running races for a given year.
-
-        TODO: this has been deprecated by strava as of Nov 1 2021 - See
-        https://developers.strava.com/docs/changelog/
-        https://developers.strava.com/docs/reference/#api-RunningRaces-getRunningRaces
-
-        :param year: year for the races (default current)
-
-        :return: An iterator of :class:`stravalib.model.RunningRace` objects.
-        :rtype: :class:`BatchedResultsIterator`
-        """
-        if year is None:
-            year = datetime.datetime.now().year
-
-        params = {"year": year}
-
-        result_fetcher = functools.partial(self.protocol.get,
-                                           '/running_races',
-                                           **params)
-
-        return BatchedResultsIterator(entity=model.RunningRace, bind_client=self,
-                                      result_fetcher=result_fetcher)
-
-
     def get_routes(self, athlete_id=None, limit=None):
         """
         Gets the routes list for an authenticated user.


Test patch:
diff --git a/stravalib/tests/unit/test_attributes.py b/stravalib/tests/unit/test_attributes.py
index dd6fe43..28b64b5 100644
--- a/stravalib/tests/unit/test_attributes.py
+++ b/stravalib/tests/unit/test_attributes.py
@@ -1,8 +1,10 @@
 from __future__ import division, absolute_import, print_function, unicode_literals
 
+import pytz
 import six
 
-from stravalib.attributes import EntityAttribute, SUMMARY, DETAILED, ChoicesAttribute, LocationAttribute, LatLon
+from stravalib.attributes import EntityAttribute, SUMMARY, DETAILED, ChoicesAttribute, LocationAttribute, LatLon, \
+    TimezoneAttribute
 from stravalib.model import Athlete, SubscriptionCallback
 from stravalib.tests import TestBase
 
@@ -56,6 +58,20 @@ class LocationAttributeTest(TestBase):
         self.assertIsNone(location.unmarshal([]))
 
 
+class TimezoneAttributeTest(TestBase):
+    def test_with_correct_timezone(self):
+        timezone = TimezoneAttribute((SUMMARY, DETAILED))
+        self.assertEqual(
+            pytz.timezone('Europe/Amsterdam'),
+            timezone.unmarshal('(GMT+01:00) Europe/Amsterdam')
+        )
+
+    def test_with_incorrect_timezone(self):
+        timezone = TimezoneAttribute((SUMMARY, DETAILED))
+        # These appear sometimes from Strava
+        self.assertIsNone(timezone.unmarshal('(GMT+00:00) Factory'))
+
+
 class ChoicesAttributeTest(TestBase):
 
     def test_no_choices_kwarg_means_choices_empty_dict(self):