Commit 175ddb5b authored by Brett Cannon's avatar Brett Cannon

Remove caching of TimeRE (and thus LocaleTime) instance. Error was being

caught when executing test_strptime, test_logging, and test_time in that order
when the testing of "%c" occured.  Suspect the cache was not being recreated
(the test passed when test_logging was forced to re-establish the locale).
parent 98741af1
...@@ -27,19 +27,15 @@ __all__ = ['strptime'] ...@@ -27,19 +27,15 @@ __all__ = ['strptime']
def _getlang(): def _getlang():
# Figure out what the current language is set to. # Figure out what the current language is set to.
current_lang = locale.getlocale(locale.LC_TIME)[0] return locale.getlocale(locale.LC_TIME)
if current_lang:
return current_lang
else:
current_lang = locale.getdefaultlocale()[0]
if current_lang:
return current_lang
else:
return ''
class LocaleTime(object): class LocaleTime(object):
"""Stores and handles locale-specific information related to time. """Stores and handles locale-specific information related to time.
This is not thread-safe! Attributes are lazily calculated and no
precaution is taken to check to see if the locale information has changed
since the creation of the instance in use.
ATTRIBUTES (all read-only after instance creation! Instance variables that ATTRIBUTES (all read-only after instance creation! Instance variables that
store the values have mangled names): store the values have mangled names):
f_weekday -- full weekday names (7-item list) f_weekday -- full weekday names (7-item list)
...@@ -103,7 +99,10 @@ class LocaleTime(object): ...@@ -103,7 +99,10 @@ class LocaleTime(object):
raise TypeError("timezone names must contain 2 items") raise TypeError("timezone names must contain 2 items")
else: else:
self.__timezone = self.__pad(timezone, False) self.__timezone = self.__pad(timezone, False)
self.__lang = lang if lang:
self.__lang = lang
else:
self.__lang = _getlang()
def __pad(self, seq, front): def __pad(self, seq, front):
# Add '' to seq to either front (is True), else the back. # Add '' to seq to either front (is True), else the back.
...@@ -196,13 +195,7 @@ class LocaleTime(object): ...@@ -196,13 +195,7 @@ class LocaleTime(object):
LC_time = property(__get_LC_time, __set_nothing, LC_time = property(__get_LC_time, __set_nothing,
doc="Format string for locale's time representation ('%X' format)") doc="Format string for locale's time representation ('%X' format)")
def __get_lang(self): lang = property(lambda self: self.__lang, __set_nothing,
# Fetch self.lang.
if not self.__lang:
self.__calc_lang()
return self.__lang
lang = property(__get_lang, __set_nothing,
doc="Language used for instance") doc="Language used for instance")
def __calc_weekday(self): def __calc_weekday(self):
...@@ -295,11 +288,6 @@ class LocaleTime(object): ...@@ -295,11 +288,6 @@ class LocaleTime(object):
time_zones.append(time.tzname[0]) time_zones.append(time.tzname[0])
self.__timezone = self.__pad(time_zones, 0) self.__timezone = self.__pad(time_zones, 0)
def __calc_lang(self):
# Set self.__lang by using __getlang().
self.__lang = _getlang()
class TimeRE(dict): class TimeRE(dict):
"""Handle conversion from format directives to regexes.""" """Handle conversion from format directives to regexes."""
...@@ -406,28 +394,12 @@ class TimeRE(dict): ...@@ -406,28 +394,12 @@ class TimeRE(dict):
"""Return a compiled re object for the format string.""" """Return a compiled re object for the format string."""
return re_compile(self.pattern(format), IGNORECASE) return re_compile(self.pattern(format), IGNORECASE)
# Cached TimeRE; probably only need one instance ever so cache it for performance
_locale_cache = TimeRE()
# Cached regex objects; same reason as for TimeRE cache
_regex_cache = dict()
def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a time struct based on the input data and the format string.""" """Return a time struct based on the input data and the format string."""
global _locale_cache time_re = TimeRE()
global _regex_cache locale_time = time_re.locale_time
locale_time = _locale_cache.locale_time format_regex = time_re.compile(format)
# If the language changes, caches are invalidated, so clear them
if locale_time.lang != _getlang():
_locale_cache = TimeRE()
_regex_cache.clear()
format_regex = _regex_cache.get(format)
if not format_regex:
# Limit regex cache size to prevent major bloating of the module;
# The value 5 is arbitrary
if len(_regex_cache) > 5:
_regex_cache.clear()
format_regex = _locale_cache.compile(format)
_regex_cache[format] = format_regex
found = format_regex.match(data_string) found = format_regex.match(data_string)
if not found: if not found:
raise ValueError("time data did not match format: data=%s fmt=%s" % raise ValueError("time data did not match format: data=%s fmt=%s" %
......
...@@ -8,6 +8,11 @@ from test import test_support ...@@ -8,6 +8,11 @@ from test import test_support
import _strptime import _strptime
class getlang_Tests(unittest.TestCase):
"""Test _getlang"""
def test_basic(self):
self.failUnlessEqual(_strptime._getlang(), locale.getlocale(locale.LC_TIME))
class LocaleTime_Tests(unittest.TestCase): class LocaleTime_Tests(unittest.TestCase):
"""Tests for _strptime.LocaleTime.""" """Tests for _strptime.LocaleTime."""
...@@ -89,11 +94,9 @@ class LocaleTime_Tests(unittest.TestCase): ...@@ -89,11 +94,9 @@ class LocaleTime_Tests(unittest.TestCase):
"empty strings") "empty strings")
def test_lang(self): def test_lang(self):
# Make sure lang is set # Make sure lang is set to what _getlang() returns
self.failUnless(self.LT_ins.lang in (locale.getdefaultlocale()[0], # Assuming locale has not changed between now and when self.LT_ins was created
locale.getlocale(locale.LC_TIME)[0], self.failUnlessEqual(self.LT_ins.lang, _strptime._getlang())
''),
"Setting of lang failed")
def test_by_hand_input(self): def test_by_hand_input(self):
# Test passed-in initialization value checks # Test passed-in initialization value checks
...@@ -410,15 +413,15 @@ class CalculationTests(unittest.TestCase): ...@@ -410,15 +413,15 @@ class CalculationTests(unittest.TestCase):
self.failUnless(result.tm_wday == self.time_tuple.tm_wday, self.failUnless(result.tm_wday == self.time_tuple.tm_wday,
"Calculation of day of the week failed;" "Calculation of day of the week failed;"
"%s != %s" % (result.tm_wday, self.time_tuple.tm_wday)) "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday))
def test_main(): def test_main():
test_support.run_unittest( test_support.run_unittest(
getlang_Tests,
LocaleTime_Tests, LocaleTime_Tests,
TimeRETests, TimeRETests,
StrptimeTests, StrptimeTests,
Strptime12AMPMTests, Strptime12AMPMTests,
JulianTests, JulianTests,
CalculationTests CalculationTests,
) )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment