Added beginnings of TimezoneLoader class to allow better extensibility of timezone loading.
In addition, turned all_timezones and common_timezones into lazy lists which do not perform file checks until evaluated.
diff --git a/pytz/__init__.py b/pytz/__init__.py
index ad42f25..af9661f 100644
--- a/pytz/__init__.py
+++ b/pytz/__init__.py
@@ -27,6 +27,7 @@
import sys, datetime, os.path, gettext
from UserDict import DictMixin
+from UserList import UserList
try:
from pkg_resources import resource_stream
@@ -43,34 +44,48 @@
except NameError:
from sets import Set as set
+class TimezoneLoader(object):
+ def __init__(self):
+ self.available = {}
+
+ def open_resource(self, name):
+ """Open a resource from the zoneinfo subdir for reading.
+
+ Uses the pkg_resources module if available and no standard file
+ found at the calculated location.
+ """
+ name_parts = name.lstrip('/').split('/')
+ for part in name_parts:
+ if part == os.path.pardir or os.path.sep in part:
+ raise ValueError('Bad path segment: %r' % part)
+ filename = os.path.join(os.path.dirname(__file__),
+ 'zoneinfo', *name_parts)
+ if not os.path.exists(filename) and resource_stream is not None:
+ # http://bugs.launchpad.net/bugs/383171 - we avoid using this
+ # unless absolutely necessary to help when a broken version of
+ # pkg_resources is installed.
+ return resource_stream(__name__, 'zoneinfo/' + name)
+ return open(filename, 'rb')
+
+
+ def resource_exists(self, name):
+ """Return true if the given resource exists"""
+ if name not in self.available:
+ try:
+ self.open_resource(name)
+ self.available[name] = True
+ except IOError:
+ self.available[name] = False
+
+ return self.available[name]
+
+loader = TimezoneLoader()
def open_resource(name):
- """Open a resource from the zoneinfo subdir for reading.
-
- Uses the pkg_resources module if available and no standard file
- found at the calculated location.
- """
- name_parts = name.lstrip('/').split('/')
- for part in name_parts:
- if part == os.path.pardir or os.path.sep in part:
- raise ValueError('Bad path segment: %r' % part)
- filename = os.path.join(os.path.dirname(__file__),
- 'zoneinfo', *name_parts)
- if not os.path.exists(filename) and resource_stream is not None:
- # http://bugs.launchpad.net/bugs/383171 - we avoid using this
- # unless absolutely necessary to help when a broken version of
- # pkg_resources is installed.
- return resource_stream(__name__, 'zoneinfo/' + name)
- return open(filename, 'rb')
-
+ return loader.open_resource(name)
def resource_exists(name):
- """Return true if the given resource exists"""
- try:
- open_resource(name)
- return True
- except IOError:
- return False
+ return loader.resource_exists(name)
# Enable this when we get some translations?
@@ -151,7 +166,7 @@
zone = _unmunge_zone(zone)
if zone not in _tzinfo_cache:
- if zone in all_timezones_set:
+ if resource_exists(zone):
_tzinfo_cache[zone] = build_tzinfo(zone, open_resource(zone))
else:
raise UnknownTimeZoneError(zone)
@@ -271,6 +286,18 @@
return self.data.keys()
+class _LazyList(UserList):
+ def __init__(self, func):
+ self._data = None
+ self._build = func
+
+ def data(self):
+ if self._data is None:
+ self._data = self._build()
+ return self._data
+
+ data = property(data)
+
class _CountryTimezoneDict(_LazyDict):
"""Map ISO 3166 country code to a list of timezone names commonly used
in that country.
@@ -305,7 +332,7 @@
if line.startswith('#'):
continue
code, coordinates, zone = line.split(None, 4)[:3]
- if zone not in all_timezones_set:
+ if not resource_exists(zone):
continue
try:
data[code].append(zone)
@@ -449,7 +476,7 @@
if __name__ == '__main__':
_test()
-all_timezones = \
+all_timezones_unfiltered = \
['Africa/Abidjan',
'Africa/Accra',
'Africa/Addis_Ababa',
@@ -1010,11 +1037,14 @@
'W-SU',
'WET',
'Zulu']
-#all_timezones = [
-# tz for tz in all_timezones if resource_exists(tz)]
-all_timezones_set = set(all_timezones)
-common_timezones = \
+all_timezones = _LazyList(
+ lambda: filter(resource_exists, all_timezones_unfiltered)
+)
+
+all_timezones_set = set(all_timezones_unfiltered) # XXX
+
+common_timezones_unfiltered = \
['Africa/Abidjan',
'Africa/Accra',
'Africa/Addis_Ababa',
@@ -1409,7 +1439,9 @@
'US/Mountain',
'US/Pacific',
'UTC']
-#common_timezones = [
-# tz for tz in common_timezones if tz in all_timezones]
-common_timezones_set = set(common_timezones)
+common_timezones = _LazyList(
+ lambda: filter(resource_exists, common_timezones_unfiltered)
+)
+
+common_timezones_set = set(common_timezones_unfiltered) # XXX