[Zope3-checkins] SVN: Zope3/branches/stub-pytzpickle/src/pytz/ pytz 2005k (prerelease)

Stuart Bishop stuart at stuartbishop.net
Sat Aug 13 23:59:07 EDT 2005


Log message for revision 37925:
  pytz 2005k (prerelease)

Changed:
  U   Zope3/branches/stub-pytzpickle/src/pytz/README.txt
  U   Zope3/branches/stub-pytzpickle/src/pytz/__init__.py
  U   Zope3/branches/stub-pytzpickle/src/pytz/reference.py
  U   Zope3/branches/stub-pytzpickle/src/pytz/tzinfo.py

-=-
Modified: Zope3/branches/stub-pytzpickle/src/pytz/README.txt
===================================================================
--- Zope3/branches/stub-pytzpickle/src/pytz/README.txt	2005-08-14 02:46:32 UTC (rev 37924)
+++ Zope3/branches/stub-pytzpickle/src/pytz/README.txt	2005-08-14 03:58:36 UTC (rev 37925)
@@ -38,7 +38,11 @@
 >>> from datetime import datetime, timedelta
 >>> from pytz import timezone
 >>> utc = timezone('UTC')
+>>> utc.zone
+'UTC'
 >>> eastern = timezone('US/Eastern')
+>>> eastern.zone
+'US/Eastern'
 >>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'
 
 The preferred way of dealing with times is to always work in UTC,

Modified: Zope3/branches/stub-pytzpickle/src/pytz/__init__.py
===================================================================
--- Zope3/branches/stub-pytzpickle/src/pytz/__init__.py	2005-08-14 02:46:32 UTC (rev 37924)
+++ Zope3/branches/stub-pytzpickle/src/pytz/__init__.py	2005-08-14 03:58:36 UTC (rev 37925)
@@ -1,7 +1,4 @@
-#!/usr/bin/env python
 '''
-$Id: __init__.py,v 1.12 2005/02/15 20:21:41 zenzen Exp $
-
 datetime.tzinfo timezone definitions generated from the
 Olson timezone database:
 
@@ -12,22 +9,71 @@
 '''
 
 # The Olson database has historically been updated about 4 times a year
-OLSON_VERSION = '2005i'
+OLSON_VERSION = '2005k'
 VERSION = OLSON_VERSION
 #VERSION = OLSON_VERSION + '.2'
+__version__ = OLSON_VERSION
 
 OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling
 
-__all__ = ['timezone', 'all_timezones', 'common_timezones', 'utc']
+__all__ = [
+    'timezone', 'all_timezones', 'common_timezones', 'utc',
+    'AmbiguousTimeError',
+    ]
 
 import sys, datetime
+from tzinfo import AmbiguousTimeError, unpickler
 
-from tzinfo import AmbiguousTimeError
+def timezone(zone):
+    ''' Return a datetime.tzinfo implementation for the given timezone 
+    
+    >>> from datetime import datetime, timedelta
+    >>> utc = timezone('UTC')
+    >>> eastern = timezone('US/Eastern')
+    >>> eastern.zone
+    'US/Eastern'
+    >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)
+    >>> loc_dt = utc_dt.astimezone(eastern)
+    >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
+    >>> loc_dt.strftime(fmt)
+    '2002-10-27 01:00:00 EST (-0500)'
+    >>> (loc_dt - timedelta(minutes=10)).strftime(fmt)
+    '2002-10-27 00:50:00 EST (-0500)'
+    >>> eastern.normalize(loc_dt - timedelta(minutes=10)).strftime(fmt)
+    '2002-10-27 01:50:00 EDT (-0400)'
+    >>> (loc_dt + timedelta(minutes=10)).strftime(fmt)
+    '2002-10-27 01:10:00 EST (-0500)'
+    '''
+    zone = _munge_zone(zone)
+    if zone.upper() == 'UTC':
+        return utc
+    zone_bits = ['zoneinfo'] + zone.split('/')
 
+    # Load zone's module
+    module_name = '.'.join(zone_bits)
+    try:
+        module = __import__(module_name, globals(), locals())
+    except ImportError:
+        raise KeyError, zone
+    rv = module
+    for bit in zone_bits[1:]:
+        rv = getattr(rv, bit)
+
+    # Return instance from that module
+    rv = getattr(rv, zone_bits[-1])
+    assert type(rv) != type(sys)
+    return rv
+
+
+def _munge_zone(zone):
+    ''' Convert a zone into a string suitable for use as a Python identifier 
+    '''
+    return zone.replace('+', '_plus_').replace('-', '_minus_')
+
+
 ZERO = datetime.timedelta(0)
 HOUR = datetime.timedelta(hours=1)
 
-# A UTC class.
 
 class UTC(datetime.tzinfo):
     """UTC
@@ -35,7 +81,11 @@
     Identical to the reference UTC implementation given in Python docs except
     that it unpickles using the single module global instance defined beneath
     this class declaration.
+
+    Also contains extra attributes and methods to match other pytz tzinfo
+    instances.
     """
+    zone = "UTC"
 
     def utcoffset(self, dt):
         return ZERO
@@ -62,11 +112,15 @@
         return dt.replace(tzinfo=self)
 
     def __repr__(self):
-        return '<UTC>'
+        return "<UTC>"
 
+    def __str__(self):
+        return "UTC"
 
-UTC = utc = UTC()
 
+UTC = utc = UTC() # UTC is a singleton
+
+
 def _UTC():
     """Factory function for utc unpickling.
     
@@ -99,50 +153,16 @@
     return utc
 _UTC.__safe_for_unpickling__ = True
 
-def timezone(zone):
-    ''' Return a datetime.tzinfo implementation for the given timezone 
-    
-    >>> from datetime import datetime, timedelta
-    >>> utc = timezone('UTC')
-    >>> eastern = timezone('US/Eastern')
-    >>> eastern.zone
-    'US/Eastern'
-    >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)
-    >>> loc_dt = utc_dt.astimezone(eastern)
-    >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
-    >>> loc_dt.strftime(fmt)
-    '2002-10-27 01:00:00 EST (-0500)'
-    >>> (loc_dt - timedelta(minutes=10)).strftime(fmt)
-    '2002-10-27 00:50:00 EST (-0500)'
-    >>> eastern.normalize(loc_dt - timedelta(minutes=10)).strftime(fmt)
-    '2002-10-27 01:50:00 EDT (-0400)'
-    >>> (loc_dt + timedelta(minutes=10)).strftime(fmt)
-    '2002-10-27 01:10:00 EST (-0500)'
-    '''
-    zone = _munge_zone(zone)
-    if zone.upper() == 'UTC':
-        return utc
-    zone_bits = ['zoneinfo'] + zone.split('/')
 
-    # Load zone's module
-    module_name = '.'.join(zone_bits)
-    try:
-        module = __import__(module_name, globals(), locals())
-    except ImportError:
-        raise KeyError, zone
-    rv = module
-    for bit in zone_bits[1:]:
-        rv = getattr(rv, bit)
+def _p(*args):
+    """Factory function for unpickling pytz tzinfo instances.
 
-    # Return instance from that module
-    rv = getattr(rv, zone_bits[-1])
-    assert type(rv) != type(sys)
-    return rv
+    Just a wrapper around tzinfo.unpickler to save a few bytes in each pickle
+    by shortening the path.
+    """
+    return unpickler(*args)
+_p.__safe_for_unpickling__ = True
 
-def _munge_zone(zone):
-    ''' Convert a zone into a string suitable for use as a Python identifier 
-    '''
-    return zone.replace('+', '_plus_').replace('-', '_minus_')
 
 def _test():
     import doctest, os, sys

Modified: Zope3/branches/stub-pytzpickle/src/pytz/reference.py
===================================================================
--- Zope3/branches/stub-pytzpickle/src/pytz/reference.py	2005-08-14 02:46:32 UTC (rev 37924)
+++ Zope3/branches/stub-pytzpickle/src/pytz/reference.py	2005-08-14 03:58:36 UTC (rev 37925)
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 '''
 $Id: reference.py,v 1.2 2004/10/25 04:14:00 zenzen Exp $
 

Modified: Zope3/branches/stub-pytzpickle/src/pytz/tzinfo.py
===================================================================
--- Zope3/branches/stub-pytzpickle/src/pytz/tzinfo.py	2005-08-14 02:46:32 UTC (rev 37924)
+++ Zope3/branches/stub-pytzpickle/src/pytz/tzinfo.py	2005-08-14 03:58:36 UTC (rev 37925)
@@ -1,10 +1,13 @@
-#!/usr/bin/env python
-'''$Id: tzinfo.py,v 1.7 2005/02/15 20:21:52 zenzen Exp $'''
+'''Base classes and helpers for building zone specific tzinfo classes'''
 
 from datetime import datetime, timedelta, tzinfo
 from bisect import bisect_right
 from sets import Set
 
+import pytz
+
+__all__ = []
+
 _timedelta_cache = {}
 def memorized_timedelta(seconds):
     '''Create only one instance of each distinct timedelta'''
@@ -41,6 +44,11 @@
 
 _notime = memorized_timedelta(0)
 
+def _to_seconds(td):
+    '''Convert a timedelta to seconds'''
+    return td.seconds + td.days * 24 * 60 * 60
+
+
 class BaseTzInfo(tzinfo):
     # Overridden in subclass
     _utcoffset = None
@@ -49,14 +57,13 @@
 
     def __str__(self):
         return self.zone
-    
 
+
 class StaticTzInfo(BaseTzInfo):
     '''A timezone that has a constant offset from UTC
 
     These timezones are rare, as most regions have changed their
     offset from UTC at some point in their history
-
     '''
     def fromutc(self, dt):
         '''See datetime.tzinfo.fromutc'''
@@ -89,7 +96,12 @@
     def __repr__(self):
         return '<StaticTzInfo %r>' % (self.zone,)
 
+    def __reduce__(self):
+        # Special pickle to zone remains a singleton and to cope with
+        # database changes. 
+        return pytz._p, (self.zone,)
 
+
 class DstTzInfo(BaseTzInfo):
     '''A timezone that has a variable offset from UTC
    
@@ -293,6 +305,17 @@
                     self.zone, self._tzname, self._utcoffset, dst
                 )
 
+    def __reduce__(self):
+        # Special pickle to zone remains a singleton and to cope with
+        # database changes.
+        return pytz._p, (
+                self.zone,
+                _to_seconds(self._utcoffset),
+                _to_seconds(self._dst),
+                self._tzname
+                )
+
+
 class AmbiguousTimeError(Exception):
     '''Exception raised when attempting to create an ambiguous wallclock time.
 
@@ -301,7 +324,56 @@
     possibilities may be correct, unless further information is supplied.
 
     See DstTzInfo.normalize() for more info
-
     '''
        
 
+def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None):
+    """Factory function for unpickling pytz tzinfo instances.
+    
+    This is shared for both StaticTzInfo and DstTzInfo instances, because
+    database changes could cause a zones implementation to switch between
+    these two base classes and we can't break pickles on a pytz version
+    upgrade.
+    """
+    # Raises a KeyError if zone no longer exists, which should never happen
+    # and would be a bug.
+    tz = pytz.timezone(zone)
+
+    # A StaticTzInfo - just return it
+    if utcoffset is None:
+        return tz
+
+    # This pickle was created from a DstTzInfo. We need to
+    # determine which of the list of tzinfo instances for this zone
+    # to use in order to restore the state of any datetime instances using
+    # it correctly.
+    utcoffset = memorized_timedelta(utcoffset)
+    dstoffset = memorized_timedelta(dstoffset)
+    try:
+        return tz._tzinfos[(utcoffset, dstoffset, tzname)]
+    except KeyError:
+        # The particular state requested in this timezone no longer exists.
+        # This indicates a corrupt pickle, or the timezone database has been
+        # corrected violently enough to make this particular
+        # (utcoffset,dstoffset) no longer exist in the zone, or the
+        # abbreviation has been changed.
+        pass
+
+    # See if we can find an entry differing only by tzname. Abbreviations
+    # get changed from the initial guess by the database maintainers to
+    # match reality when this information is discovered.
+    for localized_tz in tz._tzinfos.values():
+        if (localized_tz._utcoffset == utcoffset
+                and localized_tz._dst == dstoffset):
+            return localized_tz
+
+    # This (utcoffset, dstoffset) information has been removed from the
+    # zone. Add it back. This might occur when the database maintainers have
+    # corrected incorrect information. datetime instances using this
+    # incorrect information will continue to do so, exactly as they were
+    # before being pickled. This is purely an overly paranoid safety net - I
+    # doubt this will ever been needed in real life.
+    inf = (utcoffset, dstoffset, tzname)
+    tz._tzinfos[inf] = tz.__class__(inf, tz._tzinfos)
+    return tz._tzinfos[inf]
+



More information about the Zope3-Checkins mailing list